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