blob: f3cd6f48868f49bd6d6f26c50aa43ea8ddad441c [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
#include <utility>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/location.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_macros.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/task_runner_util.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h"
#include "components/data_reduction_proxy/core/browser/data_store.h"
#include "components/data_reduction_proxy/core/browser/network_properties_manager.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h"
#include "components/data_reduction_proxy/proto/data_store.pb.h"
#include "components/data_use_measurement/core/data_use_measurement.h"
#include "components/prefs/pref_service.h"
#include "components/previews/core/previews_experiments.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace data_reduction_proxy {
DataReductionProxyService::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)
: url_loader_factory_(std::move(url_loader_factory)),
settings_(settings),
prefs_(prefs),
db_data_owner_(new DBDataOwner(std::move(store))),
db_task_runner_(db_task_runner),
network_quality_tracker_(network_quality_tracker),
network_connection_tracker_(network_connection_tracker),
data_use_measurement_(data_use_measurement),
effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
client_(client),
channel_(channel) {
DCHECK(settings);
DCHECK(network_quality_tracker_);
DCHECK(network_connection_tracker_);
configurator_ = std::make_unique<DataReductionProxyConfigurator>();
// It is safe to use base::Unretained here, since it gets executed
// synchronously on the UI thread, and |this| outlives the caller (since the
// caller is owned by |this|.
configurator_->SetConfigUpdatedCallback(
base::BindRepeating(&DataReductionProxyService::OnProxyConfigUpdated,
base::Unretained(this)));
DataReductionProxyMutableConfigValues* raw_mutable_config = nullptr;
std::unique_ptr<DataReductionProxyMutableConfigValues> mutable_config =
std::make_unique<DataReductionProxyMutableConfigValues>();
raw_mutable_config = mutable_config.get();
config_ = std::make_unique<DataReductionProxyConfig>(
network_connection_tracker_, std::move(mutable_config),
configurator_.get());
request_options_ = std::make_unique<DataReductionProxyRequestOptions>(
client_, config_.get());
request_options_->Init();
// It is safe to use base::Unretained here, since it gets executed
// synchronously on the UI thread, and |this| outlives the caller (since the
// caller is owned by |this|.
request_options_->SetUpdateHeaderCallback(
base::BindRepeating(&DataReductionProxyService::UpdateProxyRequestHeaders,
base::Unretained(this)));
// It is safe to use base::Unretained here, since it gets executed
// synchronously on the UI thread, and |this| outlives the caller (since the
// caller is owned by |this|.
if (!params::IsIncludedInHoldbackFieldTrial() ||
previews::params::IsLitePageServerPreviewsEnabled()) {
config_client_ = std::make_unique<DataReductionProxyConfigServiceClient>(
GetBackoffPolicy(), request_options_.get(), raw_mutable_config,
config_.get(), this, network_connection_tracker_,
base::BindRepeating(&DataReductionProxyService::StoreSerializedConfig,
base::Unretained(this)));
}
network_properties_manager_ = std::make_unique<NetworkPropertiesManager>(
base::DefaultClock::GetInstance(), prefs);
// It is safe to use base::Unretained here, since it gets executed
// synchronously on the UI thread, and |this| outlives the caller (since the
// caller is owned by |this|.
config_->Initialize(
url_loader_factory_,
base::BindRepeating(&DataReductionProxyService::CreateCustomProxyConfig,
base::Unretained(this), true),
network_properties_manager_.get(), user_agent);
if (config_client_)
config_client_->Initialize(url_loader_factory_);
ReadPersistedClientConfig();
db_task_runner_->PostTask(FROM_HERE,
base::BindOnce(&DBDataOwner::InitializeOnDBThread,
db_data_owner_->GetWeakPtr()));
if (prefs_) {
compression_stats_.reset(
new DataReductionProxyCompressionStats(this, prefs_, commit_delay));
}
network_quality_tracker_->AddEffectiveConnectionTypeObserver(this);
network_quality_tracker_->AddRTTAndThroughputEstimatesObserver(this);
if (data_use_measurement_) { // null in unit tests.
data_use_measurement_->AddServicesDataUseObserver(this);
}
// TODO(rajendrant): Combine uses of NetworkConnectionTracker within DRP.
network_connection_tracker_->AddNetworkConnectionObserver(this);
network_connection_tracker_->GetConnectionType(
&connection_type_,
base::BindOnce(&DataReductionProxyService::OnConnectionChanged,
GetWeakPtr()));
}
DataReductionProxyService::~DataReductionProxyService() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
network_quality_tracker_->RemoveEffectiveConnectionTypeObserver(this);
network_quality_tracker_->RemoveRTTAndThroughputEstimatesObserver(this);
network_connection_tracker_->RemoveNetworkConnectionObserver(this);
compression_stats_.reset();
db_task_runner_->DeleteSoon(FROM_HERE, db_data_owner_.release());
if (data_use_measurement_) { // null in unit tests.
data_use_measurement_->RemoveServicesDataUseObserver(this);
}
}
void DataReductionProxyService::ReadPersistedClientConfig() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!prefs_)
return;
base::Time last_config_retrieval_time =
base::Time() + base::TimeDelta::FromMicroseconds(prefs_->GetInt64(
prefs::kDataReductionProxyLastConfigRetrievalTime));
base::TimeDelta time_since_last_config_retrieval =
base::Time::Now() - last_config_retrieval_time;
// A config older than 24 hours should not be used.
bool persisted_config_is_expired =
!last_config_retrieval_time.is_null() &&
time_since_last_config_retrieval > base::TimeDelta::FromHours(24);
if (persisted_config_is_expired)
return;
const std::string config_value =
prefs_->GetString(prefs::kDataReductionProxyConfig);
if (config_value.empty())
return;
if (config_client_)
config_client_->ApplySerializedConfig(config_value);
}
void DataReductionProxyService::OnConnectionChanged(
network::mojom::ConnectionType type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
connection_type_ = type;
}
void DataReductionProxyService::OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
effective_connection_type_ = type;
UpdateCustomProxyConfig();
}
void DataReductionProxyService::OnRTTOrThroughputEstimatesComputed(
base::TimeDelta http_rtt,
base::TimeDelta transport_rtt,
int32_t downstream_throughput_kbps) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
http_rtt_ = http_rtt;
config_->OnRTTOrThroughputEstimatesComputed(http_rtt);
}
void DataReductionProxyService::Shutdown() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
weak_factory_.InvalidateWeakPtrs();
}
void DataReductionProxyService::UpdateDataUseForHost(int64_t network_bytes,
int64_t original_bytes,
const std::string& host) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (compression_stats_) {
compression_stats_->RecordDataUseByHost(host, network_bytes, original_bytes,
base::Time::Now());
}
}
void DataReductionProxyService::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) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (compression_stats_) {
compression_stats_->RecordDataUseWithMimeType(
data_used, original_size, data_reduction_proxy_enabled, request_type,
mime_type, is_user_traffic, content_type, service_hash_code);
}
}
void DataReductionProxyService::SetUnreachable(bool unreachable) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
settings_->SetUnreachable(unreachable);
}
void DataReductionProxyService::SetInt64Pref(const std::string& pref_path,
int64_t value) {
if (prefs_)
prefs_->SetInt64(pref_path, value);
}
void DataReductionProxyService::SetStringPref(const std::string& pref_path,
const std::string& value) {
if (prefs_)
prefs_->SetString(pref_path, value);
}
void DataReductionProxyService::SetProxyPrefs(bool enabled, bool at_startup) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
config_->SetProxyConfig(enabled, at_startup);
if (config_client_) {
config_client_->SetEnabled(enabled);
if (enabled)
config_client_->RetrieveConfig();
}
// If Data Saver is disabled, reset data reduction proxy state.
if (!enabled) {
for (auto& client : proxy_config_clients_)
client->ClearBadProxiesCache();
}
}
void DataReductionProxyService::OnCacheCleared(const base::Time start,
const base::Time end) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
network_properties_manager_->DeleteHistory();
}
net::EffectiveConnectionType
DataReductionProxyService::GetEffectiveConnectionType() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return effective_connection_type_;
}
network::mojom::ConnectionType DataReductionProxyService::GetConnectionType()
const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return connection_type_;
}
base::Optional<base::TimeDelta> DataReductionProxyService::GetHttpRttEstimate()
const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return http_rtt_;
}
void DataReductionProxyService::UpdateProxyRequestHeaders(
const net::HttpRequestHeaders& headers) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
settings_->SetProxyRequestHeaders(headers);
UpdateCustomProxyConfig();
}
void DataReductionProxyService::OnProxyConfigUpdated() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
UpdateCustomProxyConfig();
UpdateThrottleConfig();
}
void DataReductionProxyService::SetIgnoreLongTermBlackListRules(
bool ignore_long_term_black_list_rules) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
settings_->SetIgnoreLongTermBlackListRules(ignore_long_term_black_list_rules);
}
void DataReductionProxyService::AddCustomProxyConfigClient(
mojo::Remote<network::mojom::CustomProxyConfigClient> config_client) {
proxy_config_clients_.Add(std::move(config_client));
UpdateCustomProxyConfig();
}
void DataReductionProxyService::LoadHistoricalDataUsage(
const HistoricalDataUsageCallback& load_data_usage_callback) {
std::unique_ptr<std::vector<DataUsageBucket>> data_usage(
new std::vector<DataUsageBucket>());
std::vector<DataUsageBucket>* data_usage_ptr = data_usage.get();
db_task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&DBDataOwner::LoadHistoricalDataUsage,
db_data_owner_->GetWeakPtr(),
base::Unretained(data_usage_ptr)),
base::BindOnce(load_data_usage_callback, std::move(data_usage)));
}
void DataReductionProxyService::LoadCurrentDataUsageBucket(
const LoadCurrentDataUsageCallback& load_current_data_usage_callback) {
std::unique_ptr<DataUsageBucket> bucket(new DataUsageBucket());
DataUsageBucket* bucket_ptr = bucket.get();
db_task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&DBDataOwner::LoadCurrentDataUsageBucket,
db_data_owner_->GetWeakPtr(),
base::Unretained(bucket_ptr)),
base::BindOnce(load_current_data_usage_callback, std::move(bucket)));
}
void DataReductionProxyService::StoreCurrentDataUsageBucket(
std::unique_ptr<DataUsageBucket> current) {
db_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&DBDataOwner::StoreCurrentDataUsageBucket,
db_data_owner_->GetWeakPtr(), std::move(current)));
}
void DataReductionProxyService::DeleteHistoricalDataUsage() {
db_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DBDataOwner::DeleteHistoricalDataUsage,
db_data_owner_->GetWeakPtr()));
}
void DataReductionProxyService::DeleteBrowsingHistory(const base::Time& start,
const base::Time& end) {
DCHECK_LE(start, end);
db_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DBDataOwner::DeleteBrowsingHistory,
db_data_owner_->GetWeakPtr(), start, end));
network_properties_manager_->DeleteHistory();
}
base::WeakPtr<DataReductionProxyService>
DataReductionProxyService::GetWeakPtr() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return weak_factory_.GetWeakPtr();
}
void DataReductionProxyService::OnServicesDataUse(int32_t service_hash_code,
int64_t recv_bytes,
int64_t sent_bytes) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (compression_stats_) {
// Record non-content initiated traffic to the Other bucket for data saver
// site-breakdown.
compression_stats_->RecordDataUseByHost(
util::GetSiteBreakdownOtherHostName(), sent_bytes, sent_bytes,
base::Time::Now());
compression_stats_->RecordDataUseByHost(
util::GetSiteBreakdownOtherHostName(), recv_bytes, recv_bytes,
base::Time::Now());
compression_stats_->RecordDataUseWithMimeType(
recv_bytes, recv_bytes, settings_->IsDataReductionProxyEnabled(), HTTPS,
std::string(), false, data_use_measurement::DataUseUserData::OTHER,
service_hash_code);
}
}
void DataReductionProxyService::MarkProxiesAsBad(
base::TimeDelta bypass_duration,
const net::ProxyList& bad_proxies,
MarkProxiesAsBadCallback callback) {
// Sanity check the inputs, as this data may originate from a lower-privilege
// process (renderer).
if (bypass_duration < base::TimeDelta()) {
LOG(ERROR) << "Received bad MarkProxiesAsBad() -- invalid bypass_duration: "
<< bypass_duration;
std::move(callback).Run();
return;
}
// Limit maximum bypass duration to a day.
if (bypass_duration > base::TimeDelta::FromDays(1))
bypass_duration = base::TimeDelta::FromDays(1);
// |bad_proxies| should be DRP servers or this API allows marking arbitrary
// proxies as bad. It is possible that proxies from an older config are
// received (FindConfiguredDataReductionProxy() searches recent proxies too).
for (const auto& proxy : bad_proxies.GetAll()) {
if (!config_->FindConfiguredDataReductionProxy(proxy)) {
LOG(ERROR) << "Received bad MarkProxiesAsBad() -- not a DRP server: "
<< proxy.ToURI();
std::move(callback).Run();
return;
}
}
for (auto& client : proxy_config_clients_)
client->MarkProxiesAsBad(bypass_duration, bad_proxies, std::move(callback));
}
void DataReductionProxyService::AddThrottleConfigObserver(
mojo::PendingRemote<mojom::DataReductionProxyThrottleConfigObserver>
observer) {
mojo::Remote<mojom::DataReductionProxyThrottleConfigObserver> observer_remote(
std::move(observer));
observer_remote->OnThrottleConfigChanged(CreateThrottleConfig());
drp_throttle_config_observers_.Add(std::move(observer_remote));
}
void DataReductionProxyService::Clone(
mojo::PendingReceiver<mojom::DataReductionProxy> receiver) {
drp_receivers_.Add(this, std::move(receiver));
}
void DataReductionProxyService::UpdateCustomProxyConfig() {
if (params::IsIncludedInHoldbackFieldTrial())
return;
network::mojom::CustomProxyConfigPtr config = CreateCustomProxyConfig(
!base::FeatureList::IsEnabled(
features::kDataReductionProxyDisableProxyFailedWarmup),
config_->GetProxiesForHttp());
for (auto& client : proxy_config_clients_)
client->OnCustomProxyConfigUpdated(config->Clone());
}
void DataReductionProxyService::UpdateThrottleConfig() {
if (drp_throttle_config_observers_.empty())
return;
auto config = CreateThrottleConfig();
for (auto& it : drp_throttle_config_observers_)
it->OnThrottleConfigChanged(config->Clone());
}
mojom::DataReductionProxyThrottleConfigPtr
DataReductionProxyService::CreateThrottleConfig() const {
return DataReductionProxyThrottleManager::CreateConfig(
config_->GetProxiesForHttp());
}
network::mojom::CustomProxyConfigPtr
DataReductionProxyService::CreateCustomProxyConfig(
bool is_warmup_url,
const std::vector<DataReductionProxyServer>& proxies_for_http) const {
auto config = network::mojom::CustomProxyConfig::New();
if (params::IsIncludedInHoldbackFieldTrial()) {
config->rules =
configurator_
->CreateProxyConfig(is_warmup_url,
config_->GetNetworkPropertiesManager(),
std::vector<DataReductionProxyServer>())
.proxy_rules();
} else {
config->rules =
configurator_
->CreateProxyConfig(is_warmup_url,
config_->GetNetworkPropertiesManager(),
proxies_for_http)
.proxy_rules();
}
net::EffectiveConnectionType type = GetEffectiveConnectionType();
if (type > net::EFFECTIVE_CONNECTION_TYPE_OFFLINE) {
DCHECK_NE(net::EFFECTIVE_CONNECTION_TYPE_LAST, type);
config->pre_cache_headers.SetHeader(
chrome_proxy_ect_header(),
net::GetNameForEffectiveConnectionType(type));
}
request_options_->AddRequestHeader(&config->post_cache_headers,
base::nullopt);
config->assume_https_proxies_support_quic = true;
config->can_use_proxy_on_http_url_redirect_cycles = false;
return config;
}
void DataReductionProxyService::StoreSerializedConfig(
const std::string& serialized_config) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!params::IsIncludedInHoldbackFieldTrial() ||
previews::params::IsLitePageServerPreviewsEnabled());
SetStringPref(prefs::kDataReductionProxyConfig, serialized_config);
SetInt64Pref(prefs::kDataReductionProxyLastConfigRetrievalTime,
(base::Time::Now() - base::Time()).InMicroseconds());
}
void DataReductionProxyService::SetDependenciesForTesting(
std::unique_ptr<DataReductionProxyConfig> config,
std::unique_ptr<DataReductionProxyRequestOptions> request_options,
std::unique_ptr<DataReductionProxyConfigurator> configurator,
std::unique_ptr<DataReductionProxyConfigServiceClient> config_client) {
config_ = std::move(config);
config_->Initialize(
url_loader_factory_,
base::BindRepeating(&DataReductionProxyService::CreateCustomProxyConfig,
base::Unretained(this), true),
network_properties_manager_.get(), std::string());
request_options_ = std::move(request_options);
request_options_->SetUpdateHeaderCallback(
base::BindRepeating(&DataReductionProxyService::UpdateProxyRequestHeaders,
base::Unretained(this)));
configurator_ = std::move(configurator);
configurator_->SetConfigUpdatedCallback(
base::BindRepeating(&DataReductionProxyService::OnProxyConfigUpdated,
base::Unretained(this)));
config_client_ = std::move(config_client);
if (config_client_)
config_client_->Initialize(url_loader_factory_);
}
} // namespace data_reduction_proxy