blob: c34ddde5e76b1160da569eadf6ac373c0f81aa78 [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_bypass_stats.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_delegate.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.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_network_delegate.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_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 "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_context_getter.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),
data_use_observer_(nullptr),
enabled_(enabled),
url_request_context_getter_(nullptr),
channel_(channel),
effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
weak_factory_(this) {
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()));
// 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|.
bypass_stats_.reset(new DataReductionProxyBypassStats(
config_.get(),
base::BindRepeating(&DataReductionProxyIOData::SetUnreachable,
base::Unretained(this)),
network_connection_tracker_));
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|.
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))));
proxy_delegate_.reset(new DataReductionProxyDelegate(
config_.get(), configurator_.get(), bypass_stats_.get()));
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),
url_request_context_getter_(nullptr),
effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
weak_factory_(this) {
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) {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
service_ = data_reduction_proxy_service;
url_request_context_getter_ = service_->url_request_context_getter();
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();
return;
}
io_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DataReductionProxyIOData::InitializeOnIOThread,
base::Unretained(this)));
}
void DataReductionProxyIOData::InitializeOnIOThread() {
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)),
network_properties_manager_.get());
bypass_stats_->InitializeOnIOThread();
proxy_delegate_->InitializeOnIOThread(this);
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();
}
std::unique_ptr<net::URLRequestInterceptor>
DataReductionProxyIOData::CreateInterceptor() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
return std::make_unique<DataReductionProxyInterceptor>(
config_.get(), config_client_.get(), bypass_stats_.get());
}
std::unique_ptr<DataReductionProxyNetworkDelegate>
DataReductionProxyIOData::CreateNetworkDelegate(
std::unique_ptr<net::NetworkDelegate> wrapped_network_delegate,
bool track_proxy_bypass_statistics) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
std::unique_ptr<DataReductionProxyNetworkDelegate> network_delegate(
new DataReductionProxyNetworkDelegate(
std::move(wrapped_network_delegate), config_.get(),
request_options_.get(), configurator_.get()));
if (track_proxy_bypass_statistics)
network_delegate->InitIODataAndUMA(this, bypass_stats_.get());
return network_delegate;
}
std::unique_ptr<DataReductionProxyDelegate>
DataReductionProxyIOData::CreateProxyDelegate() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
auto proxy_delegate = std::make_unique<DataReductionProxyDelegate>(
config_.get(), configurator_.get(), bypass_stats_.get());
proxy_delegate->InitializeOnIOThread(this);
return proxy_delegate;
}
// 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());
// TODO(crbug.com/721403): DRP is disabled with network service enabled. When
// DRP is switched to mojo, we won't need URLRequestContext.
if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
DCHECK(url_request_context_getter_->GetURLRequestContext()
->proxy_resolution_service());
}
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 (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
if (proxy_config_client_)
proxy_config_client_->ClearBadProxiesCache();
} else {
net::ProxyResolutionService* proxy_resolution_service =
url_request_context_getter_->GetURLRequestContext()
->proxy_resolution_service();
proxy_resolution_service->ClearBadProxiesCache();
}
bypass_stats_->ClearRequestCounts();
bypass_stats_->NotifyUnavailabilityIfChanged();
}
}
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::SetDataUseAscriber(
data_use_measurement::DataUseAscriber* data_use_ascriber) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
DCHECK(data_use_ascriber);
data_use_observer_.reset(
new DataReductionProxyDataUseObserver(this, data_use_ascriber));
// Disable data use ascriber when data saver is not enabled.
if (!IsEnabled()) {
data_use_ascriber->DisableAscriber();
}
}
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()));
UpdateCustomProxyConfig();
UpdateThrottleConfig();
}
network::mojom::CustomProxyConfigPtr
DataReductionProxyIOData::CreateCustomProxyConfig(
const std::vector<DataReductionProxyServer>& proxies_for_http) const {
auto config = network::mojom::CustomProxyConfig::New();
config->rules =
configurator_
->CreateProxyConfig(true /* probe_url_config */,
config_->GetNetworkPropertiesManager(),
proxies_for_http)
.proxy_rules();
// Set an alternate proxy list to be used for media requests which only
// contains proxies supporting the media resource type.
std::vector<DataReductionProxyServer> media_proxies;
for (const auto& proxy : proxies_for_http) {
if (proxy.SupportsResourceType(ResourceTypeProvider::CONTENT_TYPE_MEDIA))
media_proxies.push_back(proxy);
}
config->alternate_rules =
configurator_
->CreateProxyConfig(true /* probe_url_config */,
config_->GetNetworkPropertiesManager(),
media_proxies)
.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);
return config;
}
void DataReductionProxyIOData::UpdateCustomProxyConfig() {
if (!proxy_config_client_)
return;
proxy_config_client_->OnCustomProxyConfigUpdated(
CreateCustomProxyConfig(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();
}
DataReductionProxyThrottleManager*
DataReductionProxyIOData::GetThrottleManager() {
if (!throttle_manager_) {
mojom::DataReductionProxyPtr drp;
Clone(mojo::MakeRequest(&drp));
throttle_manager_ = std::make_unique<DataReductionProxyThrottleManager>(
std::move(drp), CreateThrottleConfig());
}
return throttle_manager_.get();
}
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).
// The current policy sets this to 5 minutes, so don't allow a bigger
// timespan.
if (bypass_duration < base::TimeDelta() ||
bypass_duration > base::TimeDelta::FromMinutes(5)) {
LOG(ERROR) << "Received bad MarkProxiesAsBad() -- invalid bypass_duration: "
<< bypass_duration;
std::move(callback).Run();
return;
}
// |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::Clone(mojom::DataReductionProxyRequest request) {
drp_bindings_.AddBinding(this, std::move(request));
}
} // namespace data_reduction_proxy