1 // Copyright 2015 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.
4 
5 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/files/file_path.h"
11 #include "base/json/json_reader.h"
12 #include "base/location.h"
13 #include "base/metrics/field_trial_params.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/task_runner_util.h"
18 #include "base/time/default_clock.h"
19 #include "base/time/time.h"
20 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h"
21 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h"
22 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
23 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
24 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h"
25 #include "components/data_reduction_proxy/core/browser/data_store.h"
26 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
27 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
28 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
29 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
30 #include "components/data_reduction_proxy/proto/data_store.pb.h"
31 #include "components/data_use_measurement/core/data_use_measurement.h"
32 #include "components/prefs/pref_service.h"
33 #include "components/previews/core/previews_experiments.h"
34 #include "mojo/public/cpp/bindings/remote.h"
35 
36 namespace data_reduction_proxy {
37 
38 namespace {
39 
GetSaveDataSavingsPercentEstimateFromFieldTrial()40 base::Optional<base::Value> GetSaveDataSavingsPercentEstimateFromFieldTrial() {
41   if (!base::FeatureList::IsEnabled(features::kReportSaveDataSavings))
42     return base::nullopt;
43   const auto origin_savings_estimate_json =
44       base::GetFieldTrialParamValueByFeature(features::kReportSaveDataSavings,
45                                              "origin_savings_estimate");
46   if (origin_savings_estimate_json.empty())
47     return base::nullopt;
48 
49   auto origin_savings_estimates =
50       base::JSONReader::Read(origin_savings_estimate_json);
51 
52   UMA_HISTOGRAM_BOOLEAN(
53       "DataReductionProxy.ReportSaveDataSavings.ParseResult",
54       origin_savings_estimates && origin_savings_estimates->is_dict());
55 
56   return origin_savings_estimates;
57 }
58 
59 }  // namespace
60 
DataReductionProxyService(DataReductionProxySettings * settings,PrefService * prefs,scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,std::unique_ptr<DataStore> store,network::NetworkQualityTracker * network_quality_tracker,network::NetworkConnectionTracker * network_connection_tracker,data_use_measurement::DataUseMeasurement * data_use_measurement,const scoped_refptr<base::SequencedTaskRunner> & db_task_runner,const base::TimeDelta & commit_delay,Client client,const std::string & channel,const std::string & user_agent)61 DataReductionProxyService::DataReductionProxyService(
62     DataReductionProxySettings* settings,
63     PrefService* prefs,
64     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
65     std::unique_ptr<DataStore> store,
66     network::NetworkQualityTracker* network_quality_tracker,
67     network::NetworkConnectionTracker* network_connection_tracker,
68     data_use_measurement::DataUseMeasurement* data_use_measurement,
69     const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
70     const base::TimeDelta& commit_delay,
71     Client client,
72     const std::string& channel,
73     const std::string& user_agent)
74     : url_loader_factory_(std::move(url_loader_factory)),
75       settings_(settings),
76       prefs_(prefs),
77       db_data_owner_(new DBDataOwner(std::move(store))),
78       db_task_runner_(db_task_runner),
79       network_quality_tracker_(network_quality_tracker),
80       network_connection_tracker_(network_connection_tracker),
81       data_use_measurement_(data_use_measurement),
82       effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
83       client_(client),
84       channel_(channel),
85       save_data_savings_estimate_dict_(
86           GetSaveDataSavingsPercentEstimateFromFieldTrial()) {
87   DCHECK(data_use_measurement_);
88   DCHECK(settings);
89   DCHECK(network_quality_tracker_);
90   DCHECK(network_connection_tracker_);
91 
92   request_options_ =
93       std::make_unique<DataReductionProxyRequestOptions>(client_);
94   request_options_->Init();
95   // It is safe to use base::Unretained here, since it gets executed
96   // synchronously on the UI thread, and |this| outlives the caller (since the
97   // caller is owned by |this|.
98   request_options_->SetUpdateHeaderCallback(
99       base::BindRepeating(&DataReductionProxyService::UpdateProxyRequestHeaders,
100                           base::Unretained(this)));
101 
102   // It is safe to use base::Unretained here, since it gets executed
103   // synchronously on the UI thread, and |this| outlives the caller (since the
104   // caller is owned by |this|.
105   if (params::ForceEnableClientConfigServiceForAllDataSaverUsers()) {
106     config_client_ = std::make_unique<DataReductionProxyConfigServiceClient>(
107         GetBackoffPolicy(), request_options_.get(), this,
108         network_connection_tracker_,
109         base::BindRepeating(&DataReductionProxyService::StoreSerializedConfig,
110                             base::Unretained(this)));
111   }
112 
113   if (config_client_)
114     config_client_->Initialize(url_loader_factory_);
115 
116   ReadPersistedClientConfig();
117 
118   db_task_runner_->PostTask(FROM_HERE,
119                             base::BindOnce(&DBDataOwner::InitializeOnDBThread,
120                                            db_data_owner_->GetWeakPtr()));
121   if (prefs_) {
122     compression_stats_.reset(
123         new DataReductionProxyCompressionStats(this, prefs_, commit_delay));
124   }
125   network_quality_tracker_->AddEffectiveConnectionTypeObserver(this);
126   network_quality_tracker_->AddRTTAndThroughputEstimatesObserver(this);
127   if (data_use_measurement_) {  // null in unit tests.
128     data_use_measurement_->AddServicesDataUseObserver(this);
129   }
130 
131   // TODO(rajendrant): Combine uses of NetworkConnectionTracker within DRP.
132   network_connection_tracker_->AddNetworkConnectionObserver(this);
133   network_connection_tracker_->GetConnectionType(
134       &connection_type_,
135       base::BindOnce(&DataReductionProxyService::OnConnectionChanged,
136                      GetWeakPtr()));
137 }
138 
~DataReductionProxyService()139 DataReductionProxyService::~DataReductionProxyService() {
140   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
141   network_quality_tracker_->RemoveEffectiveConnectionTypeObserver(this);
142   network_quality_tracker_->RemoveRTTAndThroughputEstimatesObserver(this);
143   network_connection_tracker_->RemoveNetworkConnectionObserver(this);
144   compression_stats_.reset();
145   db_task_runner_->DeleteSoon(FROM_HERE, db_data_owner_.release());
146   if (data_use_measurement_) {  // null in unit tests.
147     data_use_measurement_->RemoveServicesDataUseObserver(this);
148   }
149 }
150 
ReadPersistedClientConfig()151 void DataReductionProxyService::ReadPersistedClientConfig() {
152   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
153 
154   if (!prefs_)
155     return;
156 
157   base::Time last_config_retrieval_time =
158       base::Time() + base::TimeDelta::FromMicroseconds(prefs_->GetInt64(
159                          prefs::kDataReductionProxyLastConfigRetrievalTime));
160   base::TimeDelta time_since_last_config_retrieval =
161       base::Time::Now() - last_config_retrieval_time;
162 
163   // A config older than 24 hours should not be used.
164   bool persisted_config_is_expired =
165       !last_config_retrieval_time.is_null() &&
166       time_since_last_config_retrieval > base::TimeDelta::FromHours(24);
167 
168   if (persisted_config_is_expired)
169     return;
170 
171   const std::string config_value =
172       prefs_->GetString(prefs::kDataReductionProxyConfig);
173 
174   if (config_value.empty())
175     return;
176 
177   if (config_client_)
178     config_client_->ApplySerializedConfig(config_value);
179 }
180 
OnConnectionChanged(network::mojom::ConnectionType type)181 void DataReductionProxyService::OnConnectionChanged(
182     network::mojom::ConnectionType type) {
183   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
184   connection_type_ = type;
185 }
186 
OnEffectiveConnectionTypeChanged(net::EffectiveConnectionType type)187 void DataReductionProxyService::OnEffectiveConnectionTypeChanged(
188     net::EffectiveConnectionType type) {
189   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
190 
191   effective_connection_type_ = type;
192 }
193 
OnRTTOrThroughputEstimatesComputed(base::TimeDelta http_rtt,base::TimeDelta transport_rtt,int32_t downstream_throughput_kbps)194 void DataReductionProxyService::OnRTTOrThroughputEstimatesComputed(
195     base::TimeDelta http_rtt,
196     base::TimeDelta transport_rtt,
197     int32_t downstream_throughput_kbps) {
198   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
199   http_rtt_ = http_rtt;
200 }
201 
Shutdown()202 void DataReductionProxyService::Shutdown() {
203   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
204   weak_factory_.InvalidateWeakPtrs();
205 }
206 
UpdateDataUseForHost(int64_t network_bytes,int64_t original_bytes,const std::string & host)207 void DataReductionProxyService::UpdateDataUseForHost(int64_t network_bytes,
208                                                      int64_t original_bytes,
209                                                      const std::string& host) {
210   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
211   if (compression_stats_) {
212     compression_stats_->RecordDataUseByHost(host, network_bytes, original_bytes,
213                                             base::Time::Now());
214   }
215 }
216 
UpdateContentLengths(int64_t data_used,int64_t original_size,bool data_reduction_proxy_enabled,DataReductionProxyRequestType request_type,const std::string & mime_type,bool is_user_traffic,data_use_measurement::DataUseUserData::DataUseContentType content_type,int32_t service_hash_code)217 void DataReductionProxyService::UpdateContentLengths(
218     int64_t data_used,
219     int64_t original_size,
220     bool data_reduction_proxy_enabled,
221     DataReductionProxyRequestType request_type,
222     const std::string& mime_type,
223     bool is_user_traffic,
224     data_use_measurement::DataUseUserData::DataUseContentType content_type,
225     int32_t service_hash_code) {
226   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
227   if (compression_stats_) {
228     compression_stats_->RecordDataUseWithMimeType(
229         data_used, original_size, data_reduction_proxy_enabled, request_type,
230         mime_type, is_user_traffic, content_type, service_hash_code);
231   }
232 }
233 
SetUnreachable(bool unreachable)234 void DataReductionProxyService::SetUnreachable(bool unreachable) {
235   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
236   settings_->SetUnreachable(unreachable);
237 }
238 
SetInt64Pref(const std::string & pref_path,int64_t value)239 void DataReductionProxyService::SetInt64Pref(const std::string& pref_path,
240                                              int64_t value) {
241   if (prefs_)
242     prefs_->SetInt64(pref_path, value);
243 }
244 
SetStringPref(const std::string & pref_path,const std::string & value)245 void DataReductionProxyService::SetStringPref(const std::string& pref_path,
246                                               const std::string& value) {
247   if (prefs_)
248     prefs_->SetString(pref_path, value);
249 }
250 
SetProxyPrefs(bool enabled,bool at_startup)251 void DataReductionProxyService::SetProxyPrefs(bool enabled, bool at_startup) {
252   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
253 
254   if (config_client_) {
255     config_client_->SetEnabled(enabled);
256     if (enabled)
257       config_client_->RetrieveConfig();
258   }
259 
260   // If Data Saver is disabled, reset data reduction proxy state.
261   if (!enabled) {
262     for (auto& client : proxy_config_clients_)
263       client->ClearBadProxiesCache();
264   }
265 }
266 
267 net::EffectiveConnectionType
GetEffectiveConnectionType() const268 DataReductionProxyService::GetEffectiveConnectionType() const {
269   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
270   return effective_connection_type_;
271 }
272 
GetConnectionType() const273 network::mojom::ConnectionType DataReductionProxyService::GetConnectionType()
274     const {
275   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
276   return connection_type_;
277 }
278 
GetHttpRttEstimate() const279 base::Optional<base::TimeDelta> DataReductionProxyService::GetHttpRttEstimate()
280     const {
281   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
282   return http_rtt_;
283 }
284 
UpdateProxyRequestHeaders(const net::HttpRequestHeaders & headers)285 void DataReductionProxyService::UpdateProxyRequestHeaders(
286     const net::HttpRequestHeaders& headers) {
287   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
288   settings_->SetProxyRequestHeaders(headers);
289 }
290 
UpdatePrefetchProxyHosts(const std::vector<GURL> & prefetch_proxies)291 void DataReductionProxyService::UpdatePrefetchProxyHosts(
292     const std::vector<GURL>& prefetch_proxies) {
293   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
294   settings_->UpdatePrefetchProxyHosts(std::move(prefetch_proxies));
295 }
296 
OnProxyConfigUpdated()297 void DataReductionProxyService::OnProxyConfigUpdated() {
298   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
299 }
300 
AddCustomProxyConfigClient(mojo::Remote<network::mojom::CustomProxyConfigClient> config_client)301 void DataReductionProxyService::AddCustomProxyConfigClient(
302     mojo::Remote<network::mojom::CustomProxyConfigClient> config_client) {
303   proxy_config_clients_.Add(std::move(config_client));
304 }
305 
LoadHistoricalDataUsage(HistoricalDataUsageCallback load_data_usage_callback)306 void DataReductionProxyService::LoadHistoricalDataUsage(
307     HistoricalDataUsageCallback load_data_usage_callback) {
308   std::unique_ptr<std::vector<DataUsageBucket>> data_usage(
309       new std::vector<DataUsageBucket>());
310   std::vector<DataUsageBucket>* data_usage_ptr = data_usage.get();
311   db_task_runner_->PostTaskAndReply(
312       FROM_HERE,
313       base::BindOnce(&DBDataOwner::LoadHistoricalDataUsage,
314                      db_data_owner_->GetWeakPtr(),
315                      base::Unretained(data_usage_ptr)),
316       base::BindOnce(std::move(load_data_usage_callback),
317                      std::move(data_usage)));
318 }
319 
LoadCurrentDataUsageBucket(LoadCurrentDataUsageCallback load_current_data_usage_callback)320 void DataReductionProxyService::LoadCurrentDataUsageBucket(
321     LoadCurrentDataUsageCallback load_current_data_usage_callback) {
322   std::unique_ptr<DataUsageBucket> bucket(new DataUsageBucket());
323   DataUsageBucket* bucket_ptr = bucket.get();
324   db_task_runner_->PostTaskAndReply(
325       FROM_HERE,
326       base::BindOnce(&DBDataOwner::LoadCurrentDataUsageBucket,
327                      db_data_owner_->GetWeakPtr(),
328                      base::Unretained(bucket_ptr)),
329       base::BindOnce(std::move(load_current_data_usage_callback),
330                      std::move(bucket)));
331 }
332 
StoreCurrentDataUsageBucket(std::unique_ptr<DataUsageBucket> current)333 void DataReductionProxyService::StoreCurrentDataUsageBucket(
334     std::unique_ptr<DataUsageBucket> current) {
335   db_task_runner_->PostTask(
336       FROM_HERE,
337       base::BindOnce(&DBDataOwner::StoreCurrentDataUsageBucket,
338                      db_data_owner_->GetWeakPtr(), std::move(current)));
339 }
340 
DeleteHistoricalDataUsage()341 void DataReductionProxyService::DeleteHistoricalDataUsage() {
342   db_task_runner_->PostTask(
343       FROM_HERE, base::BindOnce(&DBDataOwner::DeleteHistoricalDataUsage,
344                                 db_data_owner_->GetWeakPtr()));
345 }
346 
DeleteBrowsingHistory(const base::Time & start,const base::Time & end)347 void DataReductionProxyService::DeleteBrowsingHistory(const base::Time& start,
348                                                       const base::Time& end) {
349   DCHECK_LE(start, end);
350   db_task_runner_->PostTask(
351       FROM_HERE, base::BindOnce(&DBDataOwner::DeleteBrowsingHistory,
352                                 db_data_owner_->GetWeakPtr(), start, end));
353 }
354 
355 base::WeakPtr<DataReductionProxyService>
GetWeakPtr()356 DataReductionProxyService::GetWeakPtr() {
357   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
358   return weak_factory_.GetWeakPtr();
359 }
360 
OnServicesDataUse(int32_t service_hash_code,int64_t recv_bytes,int64_t sent_bytes)361 void DataReductionProxyService::OnServicesDataUse(int32_t service_hash_code,
362                                                   int64_t recv_bytes,
363                                                   int64_t sent_bytes) {
364   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
365   if (compression_stats_) {
366     // Record non-content initiated traffic to the Other bucket for data saver
367     // site-breakdown.
368     compression_stats_->RecordDataUseByHost(
369         util::GetSiteBreakdownOtherHostName(), sent_bytes, sent_bytes,
370         base::Time::Now());
371     compression_stats_->RecordDataUseByHost(
372         util::GetSiteBreakdownOtherHostName(), recv_bytes, recv_bytes,
373         base::Time::Now());
374     compression_stats_->RecordDataUseWithMimeType(
375         recv_bytes, recv_bytes, settings_->IsDataReductionProxyEnabled(), HTTPS,
376         std::string(), false, data_use_measurement::DataUseUserData::OTHER,
377         service_hash_code);
378   }
379 }
380 
StoreSerializedConfig(const std::string & serialized_config)381 void DataReductionProxyService::StoreSerializedConfig(
382     const std::string& serialized_config) {
383   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
384   DCHECK(params::ForceEnableClientConfigServiceForAllDataSaverUsers());
385   SetStringPref(prefs::kDataReductionProxyConfig, serialized_config);
386   SetInt64Pref(prefs::kDataReductionProxyLastConfigRetrievalTime,
387                (base::Time::Now() - base::Time()).InMicroseconds());
388 }
389 
SetDependenciesForTesting(std::unique_ptr<DataReductionProxyRequestOptions> request_options,std::unique_ptr<DataReductionProxyConfigServiceClient> config_client)390 void DataReductionProxyService::SetDependenciesForTesting(
391     std::unique_ptr<DataReductionProxyRequestOptions> request_options,
392     std::unique_ptr<DataReductionProxyConfigServiceClient> config_client) {
393   request_options_ = std::move(request_options);
394   request_options_->SetUpdateHeaderCallback(
395       base::BindRepeating(&DataReductionProxyService::UpdateProxyRequestHeaders,
396                           base::Unretained(this)));
397 
398   config_client_ = std::move(config_client);
399 
400   if (config_client_)
401     config_client_->Initialize(url_loader_factory_);
402 }
403 
GetSaveDataSavingsPercentEstimate(const std::string & origin) const404 double DataReductionProxyService::GetSaveDataSavingsPercentEstimate(
405     const std::string& origin) const {
406   if (origin.empty() || !save_data_savings_estimate_dict_ ||
407       !save_data_savings_estimate_dict_->is_dict()) {
408     return 0;
409   }
410   const auto savings_percent =
411       save_data_savings_estimate_dict_->FindDoubleKey(origin);
412   if (!savings_percent)
413     return 0;
414   return *savings_percent;
415 }
416 
417 }  // namespace data_reduction_proxy
418