blob: 5984d33fb055d1617cd00d60e2b459230282da2b [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_io_data.h"
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.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_service.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
#include "components/data_reduction_proxy/core/browser/network_properties_manager.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.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_switches.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h"
#include "net/base/load_flags.h"
#include "net/http/http_request_headers.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/network_context.mojom.h"
namespace data_reduction_proxy {
DataReductionProxyIOData::DataReductionProxyIOData(
Client client,
PrefService* prefs,
network::NetworkConnectionTracker* network_connection_tracker,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
bool enabled,
const std::string& user_agent,
const std::string& channel)
: client_(client),
network_connection_tracker_(network_connection_tracker),
io_task_runner_(io_task_runner),
ui_task_runner_(ui_task_runner),
enabled_(enabled),
channel_(channel),
effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
DCHECK(io_task_runner_);
DCHECK(ui_task_runner_);
configurator_.reset(new DataReductionProxyConfigurator());
configurator_->SetConfigUpdatedCallback(base::BindRepeating(
&DataReductionProxyIOData::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_.reset(new DataReductionProxyConfig(
io_task_runner, ui_task_runner, network_connection_tracker_,
std::move(mutable_config), configurator_.get()));
request_options_.reset(
new DataReductionProxyRequestOptions(client_, config_.get()));
request_options_->Init();
// It is safe to use base::Unretained here, since it gets executed
// synchronously on the IO thread, and |this| outlives the caller (since the
// caller is owned by |this|.
request_options_->SetUpdateHeaderCallback(base::BindRepeating(
&DataReductionProxyIOData::UpdateProxyRequestHeaders,
base::Unretained(this)));
// It is safe to use base::Unretained here, since it gets executed
// synchronously on the IO thread, and |this| outlives the caller (since the
// caller is owned by |this|.
if (!params::IsIncludedInHoldbackFieldTrial()) {
config_client_.reset(new DataReductionProxyConfigServiceClient(
GetBackoffPolicy(), request_options_.get(), raw_mutable_config,
config_.get(), this, network_connection_tracker_,
base::BindRepeating(&DataReductionProxyIOData::StoreSerializedConfig,
base::Unretained(this))));
}
network_properties_manager_.reset(new NetworkPropertiesManager(
base::DefaultClock::GetInstance(), prefs, ui_task_runner_));
}
DataReductionProxyIOData::DataReductionProxyIOData(
PrefService* prefs,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
: client_(Client::UNKNOWN),
network_connection_tracker_(nullptr),
io_task_runner_(io_task_runner),
ui_task_runner_(ui_task_runner),
effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
DCHECK(ui_task_runner_);
DCHECK(io_task_runner_);
network_properties_manager_.reset(new NetworkPropertiesManager(
base::DefaultClock::GetInstance(), prefs, ui_task_runner_));
}
DataReductionProxyIOData::~DataReductionProxyIOData() {
// Guaranteed to be destroyed on IO thread if the IO thread is still
// available at the time of destruction. If the IO thread is unavailable,
// then the destruction will happen on the UI thread.
}
void DataReductionProxyIOData::ShutdownOnUIThread() {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
network_properties_manager_->ShutdownOnUIThread();
}
void DataReductionProxyIOData::SetDataReductionProxyService(
base::WeakPtr<DataReductionProxyService> data_reduction_proxy_service,
const std::string& user_agent) {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
service_ = data_reduction_proxy_service;
url_loader_factory_info_ = service_->url_loader_factory_info();
// Using base::Unretained is safe here, unless the browser is being shut down
// before the Initialize task can be executed. The task is only created as
// part of class initialization.
if (io_task_runner_->BelongsToCurrentThread()) {
InitializeOnIOThread(user_agent);
return;
}
io_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DataReductionProxyIOData::InitializeOnIOThread,
base::Unretained(this), user_agent));
}
void DataReductionProxyIOData::InitializeOnIOThread(
const std::string& user_agent) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
DCHECK(network_properties_manager_);
DCHECK(url_loader_factory_info_);
auto url_loader_factory = network::SharedURLLoaderFactory::Create(
std::move(url_loader_factory_info_));
config_->InitializeOnIOThread(
url_loader_factory,
base::BindRepeating(&DataReductionProxyIOData::CreateCustomProxyConfig,
base::Unretained(this), true),
network_properties_manager_.get(), user_agent);
if (config_client_)
config_client_->InitializeOnIOThread(url_loader_factory);
if (ui_task_runner_->BelongsToCurrentThread()) {
service_->SetIOData(weak_factory_.GetWeakPtr());
return;
}
ui_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DataReductionProxyService::SetIOData, service_,
weak_factory_.GetWeakPtr()));
}
bool DataReductionProxyIOData::IsEnabled() const {
DCHECK(io_task_runner_->BelongsToCurrentThread());
return enabled_;
}
void DataReductionProxyIOData::SetPingbackReportingFraction(
float pingback_reporting_fraction) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&DataReductionProxyService::SetPingbackReportingFraction,
service_, pingback_reporting_fraction));
}
void DataReductionProxyIOData::DeleteBrowsingHistory(const base::Time start,
const base::Time end) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
network_properties_manager_->DeleteHistory();
}
void DataReductionProxyIOData::OnCacheCleared(const base::Time start,
const base::Time end) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
network_properties_manager_->DeleteHistory();
}
// TODO(kundaji): Rename this method to something more descriptive.
// Bug http://crbug/488190.
void DataReductionProxyIOData::SetProxyPrefs(bool enabled, bool at_startup) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
enabled_ = enabled;
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) {
if (proxy_config_client_)
proxy_config_client_->ClearBadProxiesCache();
}
}
void DataReductionProxyIOData::SetDataReductionProxyConfiguration(
const std::string& serialized_config) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (config_client_)
config_client_->ApplySerializedConfig(serialized_config);
}
void DataReductionProxyIOData::SetIgnoreLongTermBlackListRules(
bool ignore_long_term_black_list_rules) {
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&DataReductionProxyService::SetIgnoreLongTermBlackListRules, service_,
ignore_long_term_black_list_rules));
}
void DataReductionProxyIOData::UpdateDataUseForHost(int64_t network_bytes,
int64_t original_bytes,
const std::string& host) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&DataReductionProxyService::UpdateDataUseForHost, service_,
network_bytes, original_bytes, host));
}
void DataReductionProxyIOData::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(io_task_runner_->BelongsToCurrentThread());
ui_task_runner_->PostTask(
FROM_HERE,
base::BindRepeating(&DataReductionProxyService::UpdateContentLengths,
service_, data_used, original_size,
data_reduction_proxy_enabled, request_type, mime_type,
is_user_traffic, content_type, service_hash_code));
}
void DataReductionProxyIOData::SetUnreachable(bool unreachable) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
ui_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DataReductionProxyService::SetUnreachable,
service_, unreachable));
}
void DataReductionProxyIOData::SetInt64Pref(const std::string& pref_path,
int64_t value) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
ui_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DataReductionProxyService::SetInt64Pref,
service_, pref_path, value));
}
void DataReductionProxyIOData::SetStringPref(const std::string& pref_path,
const std::string& value) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
ui_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DataReductionProxyService::SetStringPref,
service_, pref_path, value));
}
void DataReductionProxyIOData::StoreSerializedConfig(
const std::string& serialized_config) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
SetStringPref(prefs::kDataReductionProxyConfig, serialized_config);
SetInt64Pref(prefs::kDataReductionProxyLastConfigRetrievalTime,
(base::Time::Now() - base::Time()).InMicroseconds());
}
void DataReductionProxyIOData::UpdateProxyRequestHeaders(
const net::HttpRequestHeaders& headers) {
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&DataReductionProxyService::SetProxyRequestHeadersOnUI,
service_, std::move(headers)));
UpdateCustomProxyConfig();
}
void DataReductionProxyIOData::OnProxyConfigUpdated() {
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&DataReductionProxyService::SetConfiguredProxiesOnUI,
service_, config_->GetAllConfiguredProxies(),
config_->GetProxiesForHttp()));
UpdateCustomProxyConfig();
UpdateThrottleConfig();
}
network::mojom::CustomProxyConfigPtr
DataReductionProxyIOData::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 DataReductionProxyIOData::UpdateCustomProxyConfig() {
if (!proxy_config_client_)
return;
proxy_config_client_->OnCustomProxyConfigUpdated(CreateCustomProxyConfig(
!base::FeatureList::IsEnabled(
features::kDataReductionProxyDisableProxyFailedWarmup),
config_->GetProxiesForHttp()));
}
void DataReductionProxyIOData::UpdateThrottleConfig() {
if (drp_throttle_config_observers_.empty())
return;
auto config = CreateThrottleConfig();
drp_throttle_config_observers_.ForAllPtrs(
[&config](mojom::DataReductionProxyThrottleConfigObserver* observer) {
observer->OnThrottleConfigChanged(config->Clone());
});
}
mojom::DataReductionProxyThrottleConfigPtr
DataReductionProxyIOData::CreateThrottleConfig() const {
return DataReductionProxyThrottleManager::CreateConfig(
config_->GetProxiesForHttp());
}
void DataReductionProxyIOData::OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType type) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
effective_connection_type_ = type;
UpdateCustomProxyConfig();
}
void DataReductionProxyIOData::OnRTTOrThroughputEstimatesComputed(
base::TimeDelta http_rtt) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
config_->OnRTTOrThroughputEstimatesComputed(http_rtt);
}
net::EffectiveConnectionType
DataReductionProxyIOData::GetEffectiveConnectionType() const {
DCHECK(io_task_runner_->BelongsToCurrentThread());
return effective_connection_type_;
}
void DataReductionProxyIOData::SetCustomProxyConfigClient(
network::mojom::CustomProxyConfigClientPtrInfo config_client_info) {
proxy_config_client_.Bind(std::move(config_client_info));
UpdateCustomProxyConfig();
}
void DataReductionProxyIOData::MarkProxiesAsBad(
base::TimeDelta bypass_duration,
const net::ProxyList& bad_proxies,
mojom::DataReductionProxy::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;
}
}
proxy_config_client_->MarkProxiesAsBad(bypass_duration, bad_proxies,
std::move(callback));
}
void DataReductionProxyIOData::AddThrottleConfigObserver(
mojom::DataReductionProxyThrottleConfigObserverPtr observer) {
observer->OnThrottleConfigChanged(CreateThrottleConfig());
drp_throttle_config_observers_.AddPtr(std::move(observer));
}
void DataReductionProxyIOData::AddThrottleConfigObserverInfo(
mojom::DataReductionProxyThrottleConfigObserverPtrInfo observer) {
AddThrottleConfigObserver(
mojom::DataReductionProxyThrottleConfigObserverPtr(std::move(observer)));
}
void DataReductionProxyIOData::Clone(mojom::DataReductionProxyRequest request) {
drp_bindings_.AddBinding(this, std::move(request));
}
} // namespace data_reduction_proxy