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),
100 params_.reset(params);
103 DataReductionProxySettings::~DataReductionProxySettings() {
104 if (params_->allowed())
105 spdy_proxy_auth_enabled_.Destroy();
106 net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
109 void DataReductionProxySettings::InitPrefMembers() {
110 DCHECK(thread_checker_.CalledOnValidThread());
111 spdy_proxy_auth_enabled_.Init(
112 prefs::kDataReductionProxyEnabled,
113 GetOriginalProfilePrefs(),
114 base::Bind(&DataReductionProxySettings::OnProxyEnabledPrefChange,
115 base::Unretained(this)));
116 data_reduction_proxy_alternative_enabled_.Init(
117 prefs::kDataReductionProxyAltEnabled,
118 GetOriginalProfilePrefs(),
120 &DataReductionProxySettings::OnProxyAlternativeEnabledPrefChange,
121 base::Unretained(this)));
124 void DataReductionProxySettings::InitDataReductionProxySettings(
126 PrefService* local_state_prefs,
127 net::URLRequestContextGetter* url_request_context_getter) {
128 DCHECK(thread_checker_.CalledOnValidThread());
130 DCHECK(local_state_prefs);
131 DCHECK(url_request_context_getter);
133 local_state_prefs_ = local_state_prefs;
134 url_request_context_getter_ = url_request_context_getter;
136 RecordDataReductionInit();
138 // Disable the proxy if it is not allowed to be used.
139 if (!params_->allowed())
142 AddDefaultProxyBypassRules();
143 net::NetworkChangeNotifier::AddIPAddressObserver(this);
145 // We set or reset the proxy pref at startup.
146 MaybeActivateDataReductionProxy(true);
149 void DataReductionProxySettings::InitDataReductionProxySettings(
151 PrefService* local_state_prefs,
152 net::URLRequestContextGetter* url_request_context_getter,
153 DataReductionProxyConfigurator* configurator) {
154 InitDataReductionProxySettings(prefs,
156 url_request_context_getter);
157 SetProxyConfigurator(configurator);
160 void DataReductionProxySettings::SetOnDataReductionEnabledCallback(
161 const base::Callback<void(bool)>& on_data_reduction_proxy_enabled) {
162 on_data_reduction_proxy_enabled_ = on_data_reduction_proxy_enabled;
163 on_data_reduction_proxy_enabled_.Run(IsDataReductionProxyEnabled());
166 void DataReductionProxySettings::SetProxyConfigurator(
167 DataReductionProxyConfigurator* configurator) {
168 DCHECK(configurator);
169 configurator_ = configurator;
172 bool DataReductionProxySettings::IsDataReductionProxyEnabled() {
173 return spdy_proxy_auth_enabled_.GetValue() || IsEnabledOnCommandLine();
177 DataReductionProxySettings::IsDataReductionProxyAlternativeEnabled() const {
178 return data_reduction_proxy_alternative_enabled_.GetValue();
181 bool DataReductionProxySettings::IsDataReductionProxyManaged() {
182 return spdy_proxy_auth_enabled_.IsManaged();
185 void DataReductionProxySettings::SetDataReductionProxyEnabled(bool enabled) {
186 DCHECK(thread_checker_.CalledOnValidThread());
187 // Prevent configuring the proxy when it is not allowed to be used.
188 if (!params_->allowed())
191 if (spdy_proxy_auth_enabled_.GetValue() != enabled) {
192 spdy_proxy_auth_enabled_.SetValue(enabled);
193 OnProxyEnabledPrefChange();
197 void DataReductionProxySettings::SetDataReductionProxyAlternativeEnabled(
199 DCHECK(thread_checker_.CalledOnValidThread());
200 // Prevent configuring the proxy when it is not allowed to be used.
201 if (!params_->alternative_allowed())
203 if (data_reduction_proxy_alternative_enabled_.GetValue() != enabled) {
204 data_reduction_proxy_alternative_enabled_.SetValue(enabled);
205 OnProxyAlternativeEnabledPrefChange();
209 int64 DataReductionProxySettings::GetDataReductionLastUpdateTime() {
210 DCHECK(thread_checker_.CalledOnValidThread());
211 PrefService* local_state = GetLocalStatePrefs();
212 int64 last_update_internal =
213 local_state->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate);
214 base::Time last_update = base::Time::FromInternalValue(last_update_internal);
215 return static_cast<int64>(last_update.ToJsTime());
218 DataReductionProxySettings::ContentLengthList
219 DataReductionProxySettings::GetDailyOriginalContentLengths() {
220 DCHECK(thread_checker_.CalledOnValidThread());
221 return GetDailyContentLengths(prefs::kDailyHttpOriginalContentLength);
224 void DataReductionProxySettings::SetUnreachable(bool unreachable) {
225 unreachable_ = unreachable;
228 bool DataReductionProxySettings::IsDataReductionProxyUnreachable() {
229 DCHECK(thread_checker_.CalledOnValidThread());
233 DataReductionProxySettings::ContentLengthList
234 DataReductionProxySettings::GetDailyReceivedContentLengths() {
235 DCHECK(thread_checker_.CalledOnValidThread());
236 return GetDailyContentLengths(prefs::kDailyHttpReceivedContentLength);
239 void DataReductionProxySettings::OnURLFetchComplete(
240 const net::URLFetcher* source) {
241 DCHECK(thread_checker_.CalledOnValidThread());
243 // The purpose of sending a request for the warmup URL is to warm the
244 // connection to the data_reduction_proxy. The result is ignored.
245 if (source == warmup_fetcher_.get())
248 DCHECK(source == fetcher_.get());
249 net::URLRequestStatus status = source->GetStatus();
250 if (status.status() == net::URLRequestStatus::FAILED) {
251 if (status.error() == net::ERR_INTERNET_DISCONNECTED) {
252 RecordProbeURLFetchResult(INTERNET_DISCONNECTED);
255 // TODO(bengr): Remove once we understand the reasons probes are failing.
256 // Probe errors are either due to fetcher-level errors or modified
257 // responses. This only tracks the former.
258 UMA_HISTOGRAM_SPARSE_SLOWLY(
259 kUMAProxyProbeURLNetError, std::abs(status.error()));
262 std::string response;
263 source->GetResponseAsString(&response);
265 if ("OK" == response.substr(0, 2)) {
266 DVLOG(1) << "The data reduction proxy is unrestricted.";
268 if (enabled_by_user_) {
269 if (restricted_by_carrier_) {
270 // The user enabled the proxy, but sometime previously in the session,
271 // the network operator had blocked the canary and restricted the user.
272 // The current network doesn't block the canary, so don't restrict the
273 // proxy configurations.
274 SetProxyConfigs(true /* enabled */,
275 IsDataReductionProxyAlternativeEnabled(),
276 false /* restricted */,
277 false /* at_startup */);
278 RecordProbeURLFetchResult(SUCCEEDED_PROXY_ENABLED);
280 RecordProbeURLFetchResult(SUCCEEDED_PROXY_ALREADY_ENABLED);
283 restricted_by_carrier_ = false;
286 DVLOG(1) << "The data reduction proxy is restricted to the configured "
287 << "fallback proxy.";
288 if (enabled_by_user_) {
289 if (!restricted_by_carrier_) {
290 // Restrict the proxy.
291 SetProxyConfigs(true /* enabled */,
292 IsDataReductionProxyAlternativeEnabled(),
293 true /* restricted */,
294 false /* at_startup */);
295 RecordProbeURLFetchResult(FAILED_PROXY_DISABLED);
297 RecordProbeURLFetchResult(FAILED_PROXY_ALREADY_DISABLED);
300 restricted_by_carrier_ = true;
303 PrefService* DataReductionProxySettings::GetOriginalProfilePrefs() {
304 DCHECK(thread_checker_.CalledOnValidThread());
308 PrefService* DataReductionProxySettings::GetLocalStatePrefs() {
309 DCHECK(thread_checker_.CalledOnValidThread());
310 return local_state_prefs_;
313 void DataReductionProxySettings::AddDefaultProxyBypassRules() {
315 DCHECK(configurator_);
316 configurator_->AddHostPatternToBypass("<local>");
317 // RFC1918 private addresses.
318 configurator_->AddHostPatternToBypass("10.0.0.0/8");
319 configurator_->AddHostPatternToBypass("172.16.0.0/12");
320 configurator_->AddHostPatternToBypass("192.168.0.0/16");
321 // RFC4193 private addresses.
322 configurator_->AddHostPatternToBypass("fc00::/7");
323 // IPV6 probe addresses.
324 configurator_->AddHostPatternToBypass("*-ds.metric.gstatic.com");
325 configurator_->AddHostPatternToBypass("*-v4.metric.gstatic.com");
328 void DataReductionProxySettings::LogProxyState(
329 bool enabled, bool restricted, bool at_startup) {
330 // This must stay a LOG(WARNING); the output is used in processing customer
332 const char kAtStartup[] = "at startup";
333 const char kByUser[] = "by user action";
334 const char kOn[] = "ON";
335 const char kOff[] = "OFF";
336 const char kRestricted[] = "(Restricted)";
337 const char kUnrestricted[] = "(Unrestricted)";
339 std::string annotated_on =
340 kOn + std::string(" ") + (restricted ? kRestricted : kUnrestricted);
342 LOG(WARNING) << "SPDY proxy " << (enabled ? annotated_on : kOff)
343 << " " << (at_startup ? kAtStartup : kByUser);
346 void DataReductionProxySettings::OnIPAddressChanged() {
347 DCHECK(thread_checker_.CalledOnValidThread());
348 if (enabled_by_user_) {
349 DCHECK(params_->allowed());
350 RecordNetworkChangeEvent(IP_CHANGED);
353 ProbeWhetherDataReductionProxyIsAvailable();
354 WarmProxyConnection();
358 void DataReductionProxySettings::OnProxyEnabledPrefChange() {
359 DCHECK(thread_checker_.CalledOnValidThread());
360 if (!on_data_reduction_proxy_enabled_.is_null())
361 on_data_reduction_proxy_enabled_.Run(IsDataReductionProxyEnabled());
362 if (!params_->allowed())
364 MaybeActivateDataReductionProxy(false);
367 void DataReductionProxySettings::OnProxyAlternativeEnabledPrefChange() {
368 DCHECK(thread_checker_.CalledOnValidThread());
369 if (!params_->alternative_allowed())
371 MaybeActivateDataReductionProxy(false);
374 void DataReductionProxySettings::ResetDataReductionStatistics() {
375 DCHECK(thread_checker_.CalledOnValidThread());
376 PrefService* prefs = GetLocalStatePrefs();
379 ListPrefUpdate original_update(prefs, prefs::kDailyHttpOriginalContentLength);
380 ListPrefUpdate received_update(prefs, prefs::kDailyHttpReceivedContentLength);
381 original_update->Clear();
382 received_update->Clear();
383 for (size_t i = 0; i < kNumDaysInHistory; ++i) {
384 original_update->AppendString(base::Int64ToString(0));
385 received_update->AppendString(base::Int64ToString(0));
389 void DataReductionProxySettings::MaybeActivateDataReductionProxy(
391 DCHECK(thread_checker_.CalledOnValidThread());
392 PrefService* prefs = GetOriginalProfilePrefs();
393 // TODO(marq): Consider moving this so stats are wiped the first time the
394 // proxy settings are actually (not maybe) turned on.
395 if (spdy_proxy_auth_enabled_.GetValue() &&
396 !prefs->GetBoolean(prefs::kDataReductionProxyWasEnabledBefore)) {
397 prefs->SetBoolean(prefs::kDataReductionProxyWasEnabledBefore, true);
398 ResetDataReductionStatistics();
400 // Configure use of the data reduction proxy if it is enabled.
401 enabled_by_user_= IsDataReductionProxyEnabled();
402 SetProxyConfigs(enabled_by_user_ && !disabled_on_vpn_,
403 IsDataReductionProxyAlternativeEnabled(),
404 restricted_by_carrier_,
407 // Check if the proxy has been restricted explicitly by the carrier.
408 if (enabled_by_user_ && !disabled_on_vpn_) {
409 ProbeWhetherDataReductionProxyIsAvailable();
410 WarmProxyConnection();
414 void DataReductionProxySettings::SetProxyConfigs(bool enabled,
415 bool alternative_enabled,
418 DCHECK(thread_checker_.CalledOnValidThread());
419 DCHECK(configurator_);
421 LogProxyState(enabled, restricted, at_startup);
422 // The alternative is only configured if the standard configuration is
424 if (enabled & !params_->holdback()) {
425 if (alternative_enabled) {
426 configurator_->Enable(restricted,
427 !params_->fallback_allowed(),
428 params_->alt_origin().spec(),
429 params_->alt_fallback_origin().spec(),
430 params_->ssl_origin().spec());
432 configurator_->Enable(restricted,
433 !params_->fallback_allowed(),
434 params_->origin().spec(),
435 params_->fallback_origin().spec(),
439 configurator_->Disable();
444 void DataReductionProxySettings::RecordDataReductionInit() {
445 DCHECK(thread_checker_.CalledOnValidThread());
446 ProxyStartupState state = PROXY_NOT_AVAILABLE;
447 if (params_->allowed()) {
448 if (IsDataReductionProxyEnabled())
449 state = PROXY_ENABLED;
451 state = PROXY_DISABLED;
454 RecordStartupState(state);
457 void DataReductionProxySettings::RecordProbeURLFetchResult(
458 ProbeURLFetchResult result) {
459 UMA_HISTOGRAM_ENUMERATION(kUMAProxyProbeURL,
461 PROBE_URL_FETCH_RESULT_COUNT);
464 void DataReductionProxySettings::RecordStartupState(ProxyStartupState state) {
465 UMA_HISTOGRAM_ENUMERATION(kUMAProxyStartupStateHistogram,
467 PROXY_STARTUP_STATE_COUNT);
470 void DataReductionProxySettings::GetNetworkList(
471 net::NetworkInterfaceList* interfaces,
473 net::GetNetworkList(interfaces, policy);
476 void DataReductionProxySettings::ResetParamsForTest(
477 DataReductionProxyParams* params) {
478 params_.reset(params);
481 DataReductionProxySettings::ContentLengthList
482 DataReductionProxySettings::GetDailyContentLengths(const char* pref_name) {
483 DCHECK(thread_checker_.CalledOnValidThread());
484 DataReductionProxySettings::ContentLengthList content_lengths;
485 const base::ListValue* list_value = GetLocalStatePrefs()->GetList(pref_name);
486 if (list_value->GetSize() == kNumDaysInHistory) {
487 for (size_t i = 0; i < kNumDaysInHistory; ++i) {
488 content_lengths.push_back(GetInt64PrefValue(*list_value, i));
491 return content_lengths;
494 void DataReductionProxySettings::GetContentLengths(
496 int64* original_content_length,
497 int64* received_content_length,
498 int64* last_update_time) {
499 DCHECK(thread_checker_.CalledOnValidThread());
500 DCHECK_LE(days, kNumDaysInHistory);
501 PrefService* local_state = GetLocalStatePrefs();
503 *original_content_length = 0L;
504 *received_content_length = 0L;
505 *last_update_time = 0L;
509 const base::ListValue* original_list =
510 local_state->GetList(prefs::kDailyHttpOriginalContentLength);
511 const base::ListValue* received_list =
512 local_state->GetList(prefs::kDailyHttpReceivedContentLength);
514 if (original_list->GetSize() != kNumDaysInHistory ||
515 received_list->GetSize() != kNumDaysInHistory) {
516 *original_content_length = 0L;
517 *received_content_length = 0L;
518 *last_update_time = 0L;
524 // Include days from the end of the list going backwards.
525 for (size_t i = kNumDaysInHistory - days;
526 i < kNumDaysInHistory; ++i) {
527 orig += GetInt64PrefValue(*original_list, i);
528 recv += GetInt64PrefValue(*received_list, i);
530 *original_content_length = orig;
531 *received_content_length = recv;
533 local_state->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate);
536 net::URLFetcher* DataReductionProxySettings::GetBaseURLFetcher(
540 net::URLFetcher* fetcher = net::URLFetcher::Create(gurl,
541 net::URLFetcher::GET,
543 fetcher->SetLoadFlags(load_flags);
544 DCHECK(url_request_context_getter_);
545 fetcher->SetRequestContext(url_request_context_getter_);
546 // Configure max retries to be at most kMaxRetries times for 5xx errors.
547 static const int kMaxRetries = 5;
548 fetcher->SetMaxRetriesOn5xx(kMaxRetries);
549 fetcher->SetAutomaticallyRetryOnNetworkChanges(kMaxRetries);
555 DataReductionProxySettings::GetURLFetcherForAvailabilityCheck() {
556 return GetBaseURLFetcher(params_->probe_url(),
557 net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_PROXY);
561 void DataReductionProxySettings::ProbeWhetherDataReductionProxyIsAvailable() {
562 net::URLFetcher* fetcher = GetURLFetcherForAvailabilityCheck();
565 fetcher_.reset(fetcher);
569 net::URLFetcher* DataReductionProxySettings::GetURLFetcherForWarmup() {
570 return GetBaseURLFetcher(params_->warmup_url(), net::LOAD_DISABLE_CACHE);
573 void DataReductionProxySettings::WarmProxyConnection() {
574 net::URLFetcher* fetcher = GetURLFetcherForWarmup();
577 warmup_fetcher_.reset(fetcher);
578 warmup_fetcher_->Start();
581 bool DataReductionProxySettings::DisableIfVPN() {
582 net::NetworkInterfaceList network_interfaces;
583 GetNetworkList(&network_interfaces, 0);
584 // VPNs use a "tun" interface, so the presence of a "tun" interface indicates
586 // TODO(kundaji): Verify this works on Windows.
587 const std::string vpn_interface_name_prefix = "tun";
588 for (size_t i = 0; i < network_interfaces.size(); ++i) {
589 std::string interface_name = network_interfaces[i].name;
590 if (LowerCaseEqualsASCII(
591 interface_name.begin(),
592 interface_name.begin() + vpn_interface_name_prefix.size(),
593 vpn_interface_name_prefix.c_str())) {
594 SetProxyConfigs(false,
595 IsDataReductionProxyAlternativeEnabled(),
598 disabled_on_vpn_ = true;
599 RecordNetworkChangeEvent(DISABLED_ON_VPN);
603 if (disabled_on_vpn_) {
604 SetProxyConfigs(enabled_by_user_,
605 IsDataReductionProxyAlternativeEnabled(),
606 restricted_by_carrier_,
609 disabled_on_vpn_ = false;
613 } // namespace data_reduction_proxy