blob: 5171e5040085ae5d7fbb3ffc6c303780e6936ade [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_config.h"
#include <stddef.h>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/optional.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/lazy_task_runner.h"
#include "base/task_runner_util.h"
#include "base/time/default_tick_clock.h"
#include "build/build_config.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
#include "components/data_reduction_proxy/core/browser/network_properties_manager.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.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_type_info.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/previews/core/previews_decider.h"
#include "components/variations/variations_associated_data.h"
#include "net/base/host_port_pair.h"
#include "net/base/load_flags.h"
#include "net/base/network_change_notifier.h"
#include "net/base/network_interfaces.h"
#include "net/base/proxy_server.h"
#include "net/log/net_log_source_type.h"
#include "net/nqe/effective_connection_type.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_status.h"
#if defined(OS_ANDROID)
#include "net/android/network_library.h"
#endif // OS_ANDROID
using base::FieldTrialList;
namespace {
#if defined(OS_CHROMEOS)
// SequencedTaskRunner to get the network id. A SequencedTaskRunner is used
// rather than parallel tasks to avoid having many threads getting the network
// id concurrently.
base::LazySequencedTaskRunner g_get_network_id_task_runner =
LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER(
base::TaskTraits(base::MayBlock(),
base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN));
#endif
// Values of the UMA DataReductionProxy.Protocol.NotAcceptingTransform histogram
// defined in metrics/histograms/histograms.xml. This enum must remain
// synchronized with DataReductionProxyProtocolNotAcceptingTransformReason in
// tools/metrics/histograms/enums.xml.
enum NotAcceptingTransformReason {
NOT_ACCEPTING_TRANSFORM_DISABLED = 0,
NOT_ACCEPTING_TRANSFORM_BLACKLISTED = 1,
NOT_ACCEPTING_TRANSFORM_CELLULAR_ONLY = 2,
NOT_ACCEPTING_TRANSFORM_REASON_BOUNDARY
};
// Values of the UMA DataReductionProxy.NetworkChangeEvents histograms.
// This enum must remain synchronized with the enum of the same
// name in metrics/histograms/histograms.xml.
enum DataReductionProxyNetworkChangeEvent {
// The client IP address changed.
DEPRECATED_IP_CHANGED = 0,
// [Deprecated] Proxy is disabled because a VPN is running.
DEPRECATED_DISABLED_ON_VPN = 1,
// There was a network change.
NETWORK_CHANGED = 2,
CHANGE_EVENT_COUNT = NETWORK_CHANGED + 1
};
// Key of the UMA DataReductionProxy.ProbeURL histogram.
const char kUMAProxyProbeURL[] = "DataReductionProxy.ProbeURL";
// Key of the UMA DataReductionProxy.ProbeURLNetError histogram.
const char kUMAProxyProbeURLNetError[] = "DataReductionProxy.ProbeURLNetError";
// Record a network change event.
void RecordNetworkChangeEvent(DataReductionProxyNetworkChangeEvent event) {
UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.NetworkChangeEvents", event,
CHANGE_EVENT_COUNT);
}
// Records UMA containing the result of requesting the secure proxy check.
void RecordSecureProxyCheckFetchResult(
data_reduction_proxy::SecureProxyCheckFetchResult result) {
UMA_HISTOGRAM_ENUMERATION(
kUMAProxyProbeURL, result,
data_reduction_proxy::SECURE_PROXY_CHECK_FETCH_RESULT_COUNT);
}
enum class WarmupURLFetchAttemptEvent {
kFetchInitiated = 0,
kConnectionTypeNone = 1,
kProxyNotEnabledByUser = 2,
kWarmupURLFetchingDisabled = 3,
kCount
};
void RecordWarmupURLFetchAttemptEvent(
WarmupURLFetchAttemptEvent warmup_url_fetch_event) {
DCHECK_GT(WarmupURLFetchAttemptEvent::kCount, warmup_url_fetch_event);
UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.WarmupURL.FetchAttemptEvent",
warmup_url_fetch_event,
WarmupURLFetchAttemptEvent::kCount);
}
std::string DoGetCurrentNetworkID() {
// It is possible that the connection type changed between when
// GetConnectionType() was called and when the API to determine the
// network name was called. Check if that happened and retry until the
// connection type stabilizes. This is an imperfect solution but should
// capture majority of cases, and should not significantly affect estimates
// (that are approximate to begin with).
while (true) {
net::NetworkChangeNotifier::ConnectionType connection_type =
net::NetworkChangeNotifier::GetConnectionType();
std::string ssid_mccmnc;
switch (connection_type) {
case net::NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN:
case net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE:
case net::NetworkChangeNotifier::ConnectionType::CONNECTION_BLUETOOTH:
case net::NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET:
break;
case net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI:
#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_WIN)
ssid_mccmnc = net::GetWifiSSID();
#endif
break;
case net::NetworkChangeNotifier::ConnectionType::CONNECTION_2G:
case net::NetworkChangeNotifier::ConnectionType::CONNECTION_3G:
case net::NetworkChangeNotifier::ConnectionType::CONNECTION_4G:
#if defined(OS_ANDROID)
ssid_mccmnc = net::android::GetTelephonyNetworkOperator();
#endif
break;
}
if (connection_type == net::NetworkChangeNotifier::GetConnectionType()) {
if (connection_type >= net::NetworkChangeNotifier::CONNECTION_2G &&
connection_type <= net::NetworkChangeNotifier::CONNECTION_4G) {
// No need to differentiate cellular connections by the exact
// connection type.
return "cell," + ssid_mccmnc;
}
return base::IntToString(connection_type) + "," + ssid_mccmnc;
}
}
NOTREACHED();
}
} // namespace
namespace data_reduction_proxy {
DataReductionProxyConfig::DataReductionProxyConfig(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
net::NetLog* net_log,
std::unique_ptr<DataReductionProxyConfigValues> config_values,
DataReductionProxyConfigurator* configurator,
DataReductionProxyEventCreator* event_creator)
: unreachable_(false),
enabled_by_user_(false),
config_values_(std::move(config_values)),
io_task_runner_(io_task_runner),
net_log_(net_log),
configurator_(configurator),
event_creator_(event_creator),
connection_type_(net::NetworkChangeNotifier::GetConnectionType()),
ignore_long_term_black_list_rules_(false),
network_properties_manager_(nullptr),
weak_factory_(this) {
DCHECK(io_task_runner_);
DCHECK(configurator);
DCHECK(event_creator);
// Constructed on the UI thread, but should be checked on the IO thread.
thread_checker_.DetachFromThread();
}
DataReductionProxyConfig::~DataReductionProxyConfig() {
net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
}
void DataReductionProxyConfig::InitializeOnIOThread(
const scoped_refptr<net::URLRequestContextGetter>&
basic_url_request_context_getter,
const scoped_refptr<net::URLRequestContextGetter>&
url_request_context_getter,
NetworkPropertiesManager* manager) {
DCHECK(thread_checker_.CalledOnValidThread());
network_properties_manager_ = manager;
network_properties_manager_->ResetWarmupURLFetchMetrics();
secure_proxy_checker_.reset(
new SecureProxyChecker(basic_url_request_context_getter));
warmup_url_fetcher_.reset(new WarmupURLFetcher(
url_request_context_getter,
base::BindRepeating(
&DataReductionProxyConfig::HandleWarmupFetcherResponse,
base::Unretained(this)),
base::BindRepeating(&DataReductionProxyConfig::GetHttpRttEstimate,
base::Unretained(this))));
if (ShouldAddDefaultProxyBypassRules())
AddDefaultProxyBypassRules();
net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
}
bool DataReductionProxyConfig::ShouldAddDefaultProxyBypassRules() const {
DCHECK(thread_checker_.CalledOnValidThread());
return true;
}
void DataReductionProxyConfig::OnNewClientConfigFetched() {
DCHECK(thread_checker_.CalledOnValidThread());
ReloadConfig();
// Call ResetWarmupURLFetchMetrics to reset the counts since the list of
// proxies may have changed.
network_properties_manager_->ResetWarmupURLFetchMetrics();
FetchWarmupProbeURL();
}
void DataReductionProxyConfig::ReloadConfig() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(configurator_);
if (enabled_by_user_ && !params::IsIncludedInHoldbackFieldTrial() &&
!config_values_->proxies_for_http().empty()) {
configurator_->Enable(*network_properties_manager_,
config_values_->proxies_for_http());
} else {
configurator_->Disable();
}
}
base::Optional<DataReductionProxyTypeInfo>
DataReductionProxyConfig::FindConfiguredDataReductionProxy(
const net::ProxyServer& proxy_server) const {
DCHECK(thread_checker_.CalledOnValidThread());
return config_values_->FindConfiguredDataReductionProxy(proxy_server);
}
bool DataReductionProxyConfig::IsBypassedByDataReductionProxyLocalRules(
const net::URLRequest& request,
const net::ProxyConfig& data_reduction_proxy_config) const {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(request.context());
DCHECK(request.context()->proxy_resolution_service());
net::ProxyInfo result;
data_reduction_proxy_config.proxy_rules().Apply(
request.url(), &result);
if (!result.proxy_server().is_valid())
return true;
if (result.proxy_server().is_direct())
return true;
return !FindConfiguredDataReductionProxy(result.proxy_server());
}
bool DataReductionProxyConfig::AreDataReductionProxiesBypassed(
const net::URLRequest& request,
const net::ProxyConfig& data_reduction_proxy_config,
base::TimeDelta* min_retry_delay) const {
DCHECK(thread_checker_.CalledOnValidThread());
if (request.context() != nullptr &&
request.context()->proxy_resolution_service() != nullptr) {
return AreProxiesBypassed(
request.context()->proxy_resolution_service()->proxy_retry_info(),
data_reduction_proxy_config.proxy_rules(),
request.url().SchemeIsCryptographic(), min_retry_delay);
}
return false;
}
bool DataReductionProxyConfig::AreProxiesBypassed(
const net::ProxyRetryInfoMap& retry_map,
const net::ProxyConfig::ProxyRules& proxy_rules,
bool is_https,
base::TimeDelta* min_retry_delay) const {
// Data reduction proxy config is Type::PROXY_LIST_PER_SCHEME.
if (proxy_rules.type != net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME)
return false;
if (is_https)
return false;
const net::ProxyList* proxies =
proxy_rules.MapUrlSchemeToProxyList(url::kHttpScheme);
if (!proxies)
return false;
base::TimeDelta min_delay = base::TimeDelta::Max();
bool bypassed = false;
for (const net::ProxyServer& proxy : proxies->GetAll()) {
if (!proxy.is_valid() || proxy.is_direct())
continue;
base::TimeDelta delay;
if (FindConfiguredDataReductionProxy(proxy)) {
if (!IsProxyBypassed(retry_map, proxy, &delay))
return false;
if (delay < min_delay)
min_delay = delay;
bypassed = true;
}
}
if (min_retry_delay && bypassed)
*min_retry_delay = min_delay;
return bypassed;
}
bool DataReductionProxyConfig::IsProxyBypassed(
const net::ProxyRetryInfoMap& retry_map,
const net::ProxyServer& proxy_server,
base::TimeDelta* retry_delay) const {
DCHECK(thread_checker_.CalledOnValidThread());
net::ProxyRetryInfoMap::const_iterator found =
retry_map.find(proxy_server.ToURI());
if (found == retry_map.end() || found->second.bad_until < GetTicksNow()) {
return false;
}
if (retry_delay)
*retry_delay = found->second.current_delay;
return true;
}
bool DataReductionProxyConfig::ContainsDataReductionProxy(
const net::ProxyConfig::ProxyRules& proxy_rules) const {
DCHECK(thread_checker_.CalledOnValidThread());
// Data Reduction Proxy configurations are always Type::PROXY_LIST_PER_SCHEME.
if (proxy_rules.type != net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME)
return false;
const net::ProxyList* http_proxy_list =
proxy_rules.MapUrlSchemeToProxyList("http");
if (http_proxy_list && !http_proxy_list->IsEmpty() &&
// Sufficient to check only the first proxy.
FindConfiguredDataReductionProxy(http_proxy_list->Get())) {
return true;
}
return false;
}
void DataReductionProxyConfig::SetProxyConfig(bool enabled, bool at_startup) {
DCHECK(thread_checker_.CalledOnValidThread());
enabled_by_user_ = enabled;
network_properties_manager_->OnChangeInNetworkID(GetCurrentNetworkID());
ReloadConfig();
if (enabled_by_user_) {
HandleCaptivePortal();
// Check if the proxy has been restricted explicitly by the carrier.
// It is safe to use base::Unretained here, since it gets executed
// synchronously on the IO thread, and |this| outlives
// |secure_proxy_checker_|.
SecureProxyCheck(
base::Bind(&DataReductionProxyConfig::HandleSecureProxyCheckResponse,
base::Unretained(this)));
}
network_properties_manager_->ResetWarmupURLFetchMetrics();
FetchWarmupProbeURL();
}
void DataReductionProxyConfig::HandleCaptivePortal() {
DCHECK(thread_checker_.CalledOnValidThread());
bool is_captive_portal = GetIsCaptivePortal();
if (is_captive_portal == network_properties_manager_->IsCaptivePortal())
return;
network_properties_manager_->SetIsCaptivePortal(is_captive_portal);
ReloadConfig();
}
bool DataReductionProxyConfig::GetIsCaptivePortal() const {
DCHECK(thread_checker_.CalledOnValidThread());
#if defined(OS_ANDROID)
return net::android::GetIsCaptivePortal();
#endif // OS_ANDROID
return false;
}
void DataReductionProxyConfig::UpdateConfigForTesting(
bool enabled,
bool secure_proxies_allowed,
bool insecure_proxies_allowed) {
enabled_by_user_ = enabled;
network_properties_manager_->ResetWarmupURLFetchMetrics();
network_properties_manager_->SetIsSecureProxyDisallowedByCarrier(
!secure_proxies_allowed);
if (!insecure_proxies_allowed !=
network_properties_manager_->HasWarmupURLProbeFailed(
false /* secure_proxy */, false /* is_core_proxy */) ||
!insecure_proxies_allowed !=
network_properties_manager_->HasWarmupURLProbeFailed(
false /* secure_proxy */, true /* is_core_proxy */)) {
network_properties_manager_->SetHasWarmupURLProbeFailed(
false /* secure_proxy */, false /* is_core_proxy */,
!insecure_proxies_allowed);
network_properties_manager_->SetHasWarmupURLProbeFailed(
false /* secure_proxy */, true /* is_core_proxy */,
!insecure_proxies_allowed);
}
}
void DataReductionProxyConfig::SetNetworkPropertiesManagerForTesting(
NetworkPropertiesManager* manager) {
network_properties_manager_ = manager;
}
base::Optional<std::pair<bool /* is_secure_proxy */, bool /*is_core_proxy */>>
DataReductionProxyConfig::GetProxyConnectionToProbe() const {
DCHECK(thread_checker_.CalledOnValidThread());
const std::vector<DataReductionProxyServer>& proxies =
DataReductionProxyConfig::GetProxiesForHttp();
for (const DataReductionProxyServer& proxy_server : proxies) {
// First find a proxy server that has never been probed before. Proxies that
// have been probed before successfully do not need to be probed. On the
// other hand, proxies that have been probed before unsuccessfully are
// already disabled, and so they need not be probed immediately.
bool is_secure_proxy = proxy_server.IsSecureProxy();
bool is_core_proxy = proxy_server.IsCoreProxy();
if (!network_properties_manager_->HasWarmupURLProbeFailed(is_secure_proxy,
is_core_proxy) &&
network_properties_manager_->ShouldFetchWarmupProbeURL(is_secure_proxy,
is_core_proxy)) {
return std::make_pair(is_secure_proxy, is_core_proxy);
}
}
for (const DataReductionProxyServer& proxy_server : proxies) {
// Now find any proxy server that can be probed. This would return proxies
// that were probed before, the result was unsuccessful, but they have not
// yet hit the maximum probe retry limit.
bool is_secure_proxy = proxy_server.IsSecureProxy();
bool is_core_proxy = proxy_server.IsCoreProxy();
if (network_properties_manager_->ShouldFetchWarmupProbeURL(is_secure_proxy,
is_core_proxy)) {
return std::make_pair(is_secure_proxy, is_core_proxy);
}
}
// No more proxies left to probe.
return base::nullopt;
}
void DataReductionProxyConfig::HandleWarmupFetcherResponse(
const net::ProxyServer& proxy_server,
WarmupURLFetcher::FetchResult success_response) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(IsFetchInFlight());
base::Optional<DataReductionProxyTypeInfo> proxy_type_info =
FindConfiguredDataReductionProxy(proxy_server);
// Check the proxy server used.
if (!proxy_type_info && proxy_server.is_valid() &&
!proxy_server.is_direct()) {
// No need to do anything here since the warmup fetch went through
// a non-datasaver proxy.
return;
}
bool is_secure_proxy = false;
bool is_core_proxy = false;
if (proxy_type_info) {
DCHECK(proxy_server.is_valid());
DCHECK(!proxy_server.is_direct());
is_secure_proxy = proxy_server.is_https() || proxy_server.is_quic();
is_core_proxy = proxy_type_info->proxy_servers[proxy_type_info->proxy_index]
.IsCoreProxy();
// The proxy server through which the warmup URL was fetched should match
// the proxy server for which the warmup URL is in-flight.
DCHECK(GetInFlightWarmupProxyDetails());
DCHECK_EQ(is_secure_proxy, GetInFlightWarmupProxyDetails()->first);
DCHECK_EQ(is_core_proxy, GetInFlightWarmupProxyDetails()->second);
} else {
DCHECK(!proxy_server.is_valid() || proxy_server.is_direct());
// When the probe times out or if the warmup URL was fetched via DIRECT
// proxy, the data reduction proxy information may not be set. Fill-in the
// missing data using the proxy that was being probed.
is_secure_proxy = warmup_url_fetch_in_flight_secure_proxy_;
is_core_proxy = warmup_url_fetch_in_flight_core_proxy_;
}
if (is_secure_proxy && is_core_proxy) {
UMA_HISTOGRAM_BOOLEAN(
"DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
"SecureProxy.Core",
success_response == WarmupURLFetcher::FetchResult::kSuccessful);
} else if (is_secure_proxy && !is_core_proxy) {
UMA_HISTOGRAM_BOOLEAN(
"DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
"SecureProxy.NonCore",
success_response == WarmupURLFetcher::FetchResult::kSuccessful);
} else if (!is_secure_proxy && is_core_proxy) {
UMA_HISTOGRAM_BOOLEAN(
"DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
"InsecureProxy.Core",
success_response == WarmupURLFetcher::FetchResult::kSuccessful);
} else {
UMA_HISTOGRAM_BOOLEAN(
"DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
"InsecureProxy.NonCore",
success_response == WarmupURLFetcher::FetchResult::kSuccessful);
}
bool warmup_url_failed_past =
network_properties_manager_->HasWarmupURLProbeFailed(is_secure_proxy,
is_core_proxy);
network_properties_manager_->SetHasWarmupURLProbeFailed(
is_secure_proxy, is_core_proxy,
success_response !=
WarmupURLFetcher::FetchResult::kSuccessful /* warmup failed */);
if (warmup_url_failed_past !=
network_properties_manager_->HasWarmupURLProbeFailed(is_secure_proxy,
is_core_proxy)) {
ReloadConfig();
}
// May probe other proxy types that have not been probed yet, or may retry
// probe of proxy types that has failed but the maximum probe limit has not
// been reached yet. This method may have been called by warmup URL fetcher.
// FetchWarmupProbeURL() may itself call warmup URL fetcher. Posting the call
// here avoids recursive calls to the warmup URL fetcher.
io_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DataReductionProxyConfig::FetchWarmupProbeURL,
weak_factory_.GetWeakPtr()));
}
void DataReductionProxyConfig::HandleSecureProxyCheckResponse(
const std::string& response,
const net::URLRequestStatus& status,
int http_response_code) {
bool success_response =
base::StartsWith(response, "OK", base::CompareCase::SENSITIVE);
if (event_creator_) {
event_creator_->EndSecureProxyCheck(net_log_with_source_, status.error(),
http_response_code, success_response);
}
if (!status.is_success()) {
if (status.error() == net::ERR_INTERNET_DISCONNECTED) {
RecordSecureProxyCheckFetchResult(INTERNET_DISCONNECTED);
return;
}
// TODO(bengr): Remove once we understand the reasons secure proxy checks
// are failing. Secure proxy check errors are either due to fetcher-level
// errors or modified responses. This only tracks the former.
base::UmaHistogramSparse(kUMAProxyProbeURLNetError,
std::abs(status.error()));
}
bool secure_proxy_allowed_past =
!network_properties_manager_->IsSecureProxyDisallowedByCarrier();
network_properties_manager_->SetIsSecureProxyDisallowedByCarrier(
!success_response);
if (!enabled_by_user_)
return;
if (!network_properties_manager_->IsSecureProxyDisallowedByCarrier() !=
secure_proxy_allowed_past)
ReloadConfig();
// Record the result.
if (secure_proxy_allowed_past &&
!network_properties_manager_->IsSecureProxyDisallowedByCarrier()) {
RecordSecureProxyCheckFetchResult(SUCCEEDED_PROXY_ALREADY_ENABLED);
} else if (secure_proxy_allowed_past &&
network_properties_manager_->IsSecureProxyDisallowedByCarrier()) {
RecordSecureProxyCheckFetchResult(FAILED_PROXY_DISABLED);
} else if (!secure_proxy_allowed_past &&
!network_properties_manager_->IsSecureProxyDisallowedByCarrier()) {
RecordSecureProxyCheckFetchResult(SUCCEEDED_PROXY_ENABLED);
} else {
DCHECK(!secure_proxy_allowed_past &&
network_properties_manager_->IsSecureProxyDisallowedByCarrier());
RecordSecureProxyCheckFetchResult(FAILED_PROXY_ALREADY_DISABLED);
}
}
void DataReductionProxyConfig::OnNetworkChanged(
net::NetworkChangeNotifier::ConnectionType type) {
DCHECK(thread_checker_.CalledOnValidThread());
connection_type_ = type;
RecordNetworkChangeEvent(NETWORK_CHANGED);
#if defined(OS_CHROMEOS)
if (get_network_id_asynchronously_) {
base::PostTaskAndReplyWithResult(
g_get_network_id_task_runner.Get().get(), FROM_HERE,
base::BindOnce(&DoGetCurrentNetworkID),
base::BindOnce(&DataReductionProxyConfig::ContinueNetworkChanged,
weak_factory_.GetWeakPtr()));
return;
}
#endif // defined(OS_CHROMEOS)
ContinueNetworkChanged(GetCurrentNetworkID());
}
void DataReductionProxyConfig::ContinueNetworkChanged(
const std::string& network_id) {
network_properties_manager_->OnChangeInNetworkID(network_id);
ReloadConfig();
FetchWarmupProbeURL();
if (enabled_by_user_) {
HandleCaptivePortal();
// It is safe to use base::Unretained here, since it gets executed
// synchronously on the IO thread, and |this| outlives
// |secure_proxy_checker_|.
SecureProxyCheck(
base::Bind(&DataReductionProxyConfig::HandleSecureProxyCheckResponse,
base::Unretained(this)));
}
}
void DataReductionProxyConfig::AddDefaultProxyBypassRules() {
DCHECK(configurator_);
configurator_->SetBypassRules(
// localhost
"<local>,"
// RFC6890 loopback addresses.
// TODO(tbansal): Remove this once crbug/446705 is fixed.
"127.0.0.0/8,"
// RFC6890 current network (only valid as source address).
"0.0.0.0/8,"
// RFC1918 private addresses.
"10.0.0.0/8,"
"172.16.0.0/12,"
"192.168.0.0/16,"
// RFC3513 unspecified address.
"::/128,"
// RFC4193 private addresses.
"fc00::/7,"
// IPV6 probe addresses.
"*-ds.metric.gstatic.com,"
"*-v4.metric.gstatic.com");
}
void DataReductionProxyConfig::SecureProxyCheck(
SecureProxyCheckerCallback fetcher_callback) {
net_log_with_source_ = net::NetLogWithSource::Make(
net_log_, net::NetLogSourceType::DATA_REDUCTION_PROXY);
if (event_creator_) {
event_creator_->BeginSecureProxyCheck(net_log_with_source_,
params::GetSecureProxyCheckURL());
}
secure_proxy_checker_->CheckIfSecureProxyIsAllowed(fetcher_callback);
}
void DataReductionProxyConfig::FetchWarmupProbeURL() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!enabled_by_user_) {
RecordWarmupURLFetchAttemptEvent(
WarmupURLFetchAttemptEvent::kProxyNotEnabledByUser);
return;
}
if (!params::FetchWarmupProbeURLEnabled()) {
RecordWarmupURLFetchAttemptEvent(
WarmupURLFetchAttemptEvent::kWarmupURLFetchingDisabled);
return;
}
if (connection_type_ == net::NetworkChangeNotifier::CONNECTION_NONE) {
RecordWarmupURLFetchAttemptEvent(
WarmupURLFetchAttemptEvent::kConnectionTypeNone);
return;
}
base::Optional<std::pair<bool /* is_secure_proxy */, bool /*is_core_proxy */>>
warmup_config = GetProxyConnectionToProbe();
if (!warmup_config)
return;
// Refetch the warmup URL when it has failed.
warmup_url_fetch_in_flight_secure_proxy_ = warmup_config->first;
warmup_url_fetch_in_flight_core_proxy_ = warmup_config->second;
size_t previous_attempt_counts = GetWarmupURLFetchAttemptCounts();
network_properties_manager_->OnWarmupFetchInitiated(
warmup_url_fetch_in_flight_secure_proxy_,
warmup_url_fetch_in_flight_core_proxy_);
RecordWarmupURLFetchAttemptEvent(WarmupURLFetchAttemptEvent::kFetchInitiated);
warmup_url_fetcher_->FetchWarmupURL(previous_attempt_counts);
}
size_t DataReductionProxyConfig::GetWarmupURLFetchAttemptCounts() const {
DCHECK(thread_checker_.CalledOnValidThread());
return network_properties_manager_->GetWarmupURLFetchAttemptCounts(
warmup_url_fetch_in_flight_secure_proxy_,
warmup_url_fetch_in_flight_core_proxy_);
}
void DataReductionProxyConfig::OnRTTOrThroughputEstimatesComputed(
base::TimeDelta http_rtt) {
DCHECK(thread_checker_.CalledOnValidThread());
http_rtt_ = http_rtt;
}
base::Optional<base::TimeDelta> DataReductionProxyConfig::GetHttpRttEstimate()
const {
DCHECK(thread_checker_.CalledOnValidThread());
return http_rtt_;
}
bool DataReductionProxyConfig::enabled_by_user_and_reachable() const {
DCHECK(thread_checker_.CalledOnValidThread());
return enabled_by_user_ && !unreachable_;
}
bool DataReductionProxyConfig::IsBlackListedOrDisabled(
const net::URLRequest& request,
const previews::PreviewsDecider& previews_decider,
previews::PreviewsType previews_type) const {
// Make sure request is not locally blacklisted.
// Pass in net::EFFECTIVE_CONNECTION_TYPE_4G as the threshold since we
// just want to check blacklisting here.
// TODO(crbug.com/720102): Consider new method to just check blacklist.
return !previews_decider.ShouldAllowPreviewAtECT(
request, previews_type, net::EFFECTIVE_CONNECTION_TYPE_4G,
std::vector<std::string>(), ignore_long_term_black_list_rules_);
}
bool DataReductionProxyConfig::ShouldAcceptServerPreview(
const net::URLRequest& request,
const previews::PreviewsDecider& previews_decider) const {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK((request.load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED) != 0);
DCHECK(request.url().SchemeIsHTTPOrHTTPS());
if (!previews::params::ArePreviewsAllowed() ||
!base::FeatureList::IsEnabled(
features::kDataReductionProxyDecidesTransform)) {
return false;
}
if (IsBlackListedOrDisabled(request, previews_decider,
previews::PreviewsType::LITE_PAGE) ||
IsBlackListedOrDisabled(request, previews_decider,
previews::PreviewsType::LOFI)) {
UMA_HISTOGRAM_ENUMERATION(
"DataReductionProxy.Protocol.NotAcceptingTransform",
NOT_ACCEPTING_TRANSFORM_BLACKLISTED,
NOT_ACCEPTING_TRANSFORM_REASON_BOUNDARY);
return false;
}
return true;
}
base::TimeTicks DataReductionProxyConfig::GetTicksNow() const {
DCHECK(thread_checker_.CalledOnValidThread());
return base::TimeTicks::Now();
}
net::ProxyConfig DataReductionProxyConfig::ProxyConfigIgnoringHoldback() const {
if (!enabled_by_user_ || config_values_->proxies_for_http().empty())
return net::ProxyConfig::CreateDirect();
return configurator_->CreateProxyConfig(false /* probe_url_config */,
*network_properties_manager_,
config_values_->proxies_for_http());
}
std::vector<DataReductionProxyServer>
DataReductionProxyConfig::GetProxiesForHttp() const {
DCHECK(thread_checker_.CalledOnValidThread());
if (!enabled_by_user_)
return std::vector<DataReductionProxyServer>();
return config_values_->proxies_for_http();
}
std::string DataReductionProxyConfig::GetCurrentNetworkID() const {
DCHECK(thread_checker_.CalledOnValidThread());
return DoGetCurrentNetworkID();
}
const NetworkPropertiesManager&
DataReductionProxyConfig::GetNetworkPropertiesManager() const {
DCHECK(thread_checker_.CalledOnValidThread());
return *network_properties_manager_;
}
bool DataReductionProxyConfig::IsFetchInFlight() const {
DCHECK(thread_checker_.CalledOnValidThread());
return warmup_url_fetcher_->IsFetchInFlight();
}
base::Optional<std::pair<bool /* is_secure_proxy */, bool /*is_core_proxy */>>
DataReductionProxyConfig::GetInFlightWarmupProxyDetails() const {
DCHECK(thread_checker_.CalledOnValidThread());
if (!IsFetchInFlight())
return base::nullopt;
return std::make_pair(warmup_url_fetch_in_flight_secure_proxy_,
warmup_url_fetch_in_flight_core_proxy_);
}
#if defined(OS_CHROMEOS)
void DataReductionProxyConfig::EnableGetNetworkIdAsynchronously() {
get_network_id_asynchronously_ = true;
}
#endif // defined(OS_CHROMEOS)
void DataReductionProxyConfig::SetIgnoreLongTermBlackListRules(
bool ignore_long_term_black_list_rules) {
DCHECK(thread_checker_.CalledOnValidThread());
ignore_long_term_black_list_rules_ = ignore_long_term_black_list_rules;
}
bool DataReductionProxyConfig::IgnoreBlackListLongTermRulesForTesting() const {
DCHECK(thread_checker_.CalledOnValidThread());
return ignore_long_term_black_list_rules_;
}
} // namespace data_reduction_proxy