ProjectingObserverChromeos: Drop DBusThreadManager dependency for better testing.
[chromium-blink-merge.git] / components / data_reduction_proxy / browser / data_reduction_proxy_settings.cc
blob07d920cacbb6c864ab86c4d9ef1b92874b07cc5a
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"
7 #include "base/bind.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"
35 #include "url/gurl.h"
38 using base::StringPrintf;
40 namespace {
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",
63 event,
64 CHANGE_EVENT_COUNT);
67 int64 GetInt64PrefValue(const base::ListValue& list_value, size_t index) {
68 int64 val = 0;
69 std::string pref_value;
70 bool rv = list_value.GetString(index, &pref_value);
71 DCHECK(rv);
72 if (rv) {
73 rv = base::StringToInt64(pref_value, &val);
74 DCHECK(rv);
76 return val;
79 bool IsEnabledOnCommandLine() {
80 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
81 return command_line.HasSwitch(
82 data_reduction_proxy::switches::kEnableDataReductionProxy);
85 } // namespace
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),
94 unreachable_(false),
95 prefs_(NULL),
96 url_request_context_getter_(NULL),
97 configurator_(NULL) {
98 DCHECK(params);
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(),
118 base::Bind(
119 &DataReductionProxySettings::OnProxyAlternativeEnabledPrefChange,
120 base::Unretained(this)));
123 void DataReductionProxySettings::InitDataReductionProxySettings(
124 PrefService* prefs,
125 net::URLRequestContextGetter* url_request_context_getter) {
126 DCHECK(thread_checker_.CalledOnValidThread());
127 DCHECK(prefs);
128 DCHECK(url_request_context_getter);
129 prefs_ = prefs;
130 url_request_context_getter_ = url_request_context_getter;
131 InitPrefMembers();
132 RecordDataReductionInit();
134 // Disable the proxy if it is not allowed to be used.
135 if (!params_->allowed())
136 return;
138 AddDefaultProxyBypassRules();
139 net::NetworkChangeNotifier::AddIPAddressObserver(this);
141 // We set or reset the proxy pref at startup.
142 MaybeActivateDataReductionProxy(true);
145 void DataReductionProxySettings::InitDataReductionProxySettings(
146 PrefService* prefs,
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();
175 bool
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())
188 return;
190 if (spdy_proxy_auth_enabled_.GetValue() != enabled) {
191 spdy_proxy_auth_enabled_.SetValue(enabled);
192 OnProxyEnabledPrefChange();
196 void DataReductionProxySettings::SetDataReductionProxyAlternativeEnabled(
197 bool enabled) {
198 DCHECK(thread_checker_.CalledOnValidThread());
199 // Prevent configuring the proxy when it is not allowed to be used.
200 if (!params_->alternative_allowed())
201 return;
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());
229 return unreachable_;
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);
247 return;
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);
273 } else {
274 RecordProbeURLFetchResult(SUCCEEDED_PROXY_ALREADY_ENABLED);
277 restricted_by_carrier_ = false;
278 return;
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);
290 } else {
291 RecordProbeURLFetchResult(FAILED_PROXY_ALREADY_DISABLED);
294 restricted_by_carrier_ = true;
297 PrefService* DataReductionProxySettings::GetOriginalProfilePrefs() {
298 DCHECK(thread_checker_.CalledOnValidThread());
299 return prefs_;
302 void DataReductionProxySettings::AddDefaultProxyBypassRules() {
303 // localhost
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
320 // feedback.
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);
340 if (DisableIfVPN())
341 return;
342 if (IsDataReductionProxyAlternativeEnabled() &&
343 !params_->alternative_fallback_allowed()) {
344 return;
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())
355 return;
356 MaybeActivateDataReductionProxy(false);
359 void DataReductionProxySettings::OnProxyAlternativeEnabledPrefChange() {
360 DCHECK(thread_checker_.CalledOnValidThread());
361 if (!params_->alternative_allowed())
362 return;
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(
382 bool at_startup) {
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_,
397 at_startup);
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,
409 bool restricted,
410 bool at_startup) {
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
416 // is enabled.
417 if (enabled & !params_->holdback()) {
418 if (alternative_enabled) {
419 configurator_->Enable(restricted,
420 !params_->alternative_fallback_allowed(),
421 params_->alt_origin().spec(),
422 std::string(),
423 params_->ssl_origin().spec());
424 } else {
425 configurator_->Enable(restricted,
426 !params_->fallback_allowed(),
427 params_->origin().spec(),
428 params_->fallback_origin().spec(),
429 std::string());
431 } else {
432 configurator_->Disable();
436 // Metrics methods
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;
443 else
444 state = PROXY_DISABLED;
447 RecordStartupState(state);
450 void DataReductionProxySettings::RecordProbeURLFetchResult(
451 ProbeURLFetchResult result) {
452 UMA_HISTOGRAM_ENUMERATION(kUMAProxyProbeURL,
453 result,
454 PROBE_URL_FETCH_RESULT_COUNT);
457 void DataReductionProxySettings::RecordStartupState(ProxyStartupState state) {
458 UMA_HISTOGRAM_ENUMERATION(kUMAProxyStartupStateHistogram,
459 state,
460 PROXY_STARTUP_STATE_COUNT);
463 void DataReductionProxySettings::GetNetworkList(
464 net::NetworkInterfaceList* interfaces,
465 int policy) {
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(
489 unsigned int days,
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;
507 return;
510 int64 orig = 0L;
511 int64 recv = 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;
520 *last_update_time =
521 statistics_prefs_->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate);
524 net::URLFetcher* DataReductionProxySettings::GetBaseURLFetcher(
525 const GURL& gurl,
526 int load_flags) {
528 net::URLFetcher* fetcher = net::URLFetcher::Create(gurl,
529 net::URLFetcher::GET,
530 this);
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);
538 return fetcher;
542 net::URLFetcher*
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();
551 if (!fetcher)
552 return;
553 fetcher_.reset(fetcher);
554 fetcher_->Start();
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
561 // a VPN is in use.
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(),
572 false,
573 false);
574 disabled_on_vpn_ = true;
575 RecordNetworkChangeEvent(DISABLED_ON_VPN);
576 return true;
579 if (disabled_on_vpn_) {
580 SetProxyConfigs(enabled_by_user_,
581 IsDataReductionProxyAlternativeEnabled(),
582 restricted_by_carrier_,
583 false);
585 disabled_on_vpn_ = false;
586 return false;
589 } // namespace data_reduction_proxy