blob: fe51f1142a0da213f8a3b888f30be866259181c2 [file] [log] [blame]
// Copyright 2014 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 "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
#include <memory>
#include <string>
#include <utility>
#include "base/base64.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram_macros.h"
#include "base/stl_util.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/data_use_measurement/chrome_data_use_measurement.h"
#include "chrome/browser/loader/chrome_navigation_data.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/previews/previews_service.h"
#include "chrome/browser/previews/previews_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/pref_names.h"
#include "components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.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_data.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
#include "components/data_reduction_proxy/core/browser/data_store.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/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/previews/content/previews_ui_service.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "components/proxy_config/proxy_prefs.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/network_service_instance.h"
#include "net/base/host_port_pair.h"
#include "net/base/proxy_server.h"
#include "net/proxy_resolution/proxy_config.h"
#include "net/proxy_resolution/proxy_list.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/network/public/cpp/network_quality_tracker.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace {
// Assume that any proxy host ending with this suffix is a Data Reduction Proxy.
const char kDataReductionProxyDefaultHostSuffix[] = ".googlezip.net";
// Searches |proxy_list| for any Data Reduction Proxies, even if they don't
// match a currently configured Data Reduction Proxy.
bool ContainsDataReductionProxyDefaultHostSuffix(
const net::ProxyList& proxy_list) {
for (const net::ProxyServer& proxy : proxy_list.GetAll()) {
if (proxy.is_valid() && !proxy.is_direct() &&
base::EndsWith(proxy.host_port_pair().host(),
kDataReductionProxyDefaultHostSuffix,
base::CompareCase::SENSITIVE)) {
return true;
}
}
return false;
}
// Searches |proxy_rules| for any Data Reduction Proxies, even if they don't
// match a currently configured Data Reduction Proxy.
bool ContainsDataReductionProxyDefaultHostSuffix(
const net::ProxyConfig::ProxyRules& proxy_rules) {
return ContainsDataReductionProxyDefaultHostSuffix(
proxy_rules.proxies_for_http) ||
ContainsDataReductionProxyDefaultHostSuffix(
proxy_rules.proxies_for_https);
}
// Extract the embedded PAC script from the given |pac_url|, and store the
// extracted script in |pac_script|. Returns true if extraction was successful,
// otherwise returns false. |pac_script| must not be NULL.
bool GetEmbeddedPacScript(base::StringPiece pac_url, std::string* pac_script) {
DCHECK(pac_script);
static const char kPacURLPrefix[] =
"data:application/x-ns-proxy-autoconfig;base64,";
return base::StartsWith(pac_url, kPacURLPrefix,
base::CompareCase::SENSITIVE) &&
base::Base64Decode(pac_url.substr(base::size(kPacURLPrefix) - 1),
pac_script);
}
} // namespace
// The Data Reduction Proxy has been turned into a "best effort" proxy,
// meaning it is used only if the effective proxy configuration resolves to
// DIRECT for a URL. It no longer can be a ProxyConfig in the proxy preference
// hierarchy. This method removes the Data Reduction Proxy configuration from
// prefs, if present. |proxy_pref_name| is the name of the proxy pref.
void DataReductionProxyChromeSettings::MigrateDataReductionProxyOffProxyPrefs(
PrefService* prefs) {
ProxyPrefMigrationResult proxy_pref_status =
MigrateDataReductionProxyOffProxyPrefsHelper(prefs);
UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.ProxyPrefMigrationResult",
proxy_pref_status,
DataReductionProxyChromeSettings::PROXY_PREF_MAX);
}
DataReductionProxyChromeSettings::ProxyPrefMigrationResult
DataReductionProxyChromeSettings::MigrateDataReductionProxyOffProxyPrefsHelper(
PrefService* prefs) {
base::DictionaryValue* dict = (base::DictionaryValue*)prefs->GetUserPrefValue(
proxy_config::prefs::kProxy);
if (!dict)
return PROXY_PREF_NOT_CLEARED;
// Clear empty "proxy" dictionary created by a bug. See http://crbug/448172.
if (dict->empty()) {
prefs->ClearPref(proxy_config::prefs::kProxy);
return PROXY_PREF_CLEARED_EMPTY;
}
std::string mode;
if (!dict->GetString("mode", &mode))
return PROXY_PREF_NOT_CLEARED;
// Clear "system" proxy entry since this is the default. This entry was
// created by bug (http://crbug/448172).
if (ProxyModeToString(ProxyPrefs::MODE_SYSTEM) == mode) {
prefs->ClearPref(proxy_config::prefs::kProxy);
return PROXY_PREF_CLEARED_MODE_SYSTEM;
}
// From M36 to M40, the DRP was configured using MODE_FIXED_SERVERS in the
// proxy pref.
if (ProxyModeToString(ProxyPrefs::MODE_FIXED_SERVERS) == mode) {
std::string proxy_server;
if (!dict->GetString("server", &proxy_server))
return PROXY_PREF_NOT_CLEARED;
net::ProxyConfig::ProxyRules proxy_rules;
proxy_rules.ParseFromString(proxy_server);
// Clear the proxy pref if it matches a currently configured Data Reduction
// Proxy, or if the proxy host ends with ".googlezip.net", in order to
// ensure that any DRP in the pref is cleared even if the DRP configuration
// was changed. See http://crbug.com/476610.
ProxyPrefMigrationResult rv;
if (Config()->ContainsDataReductionProxy(proxy_rules))
rv = PROXY_PREF_CLEARED_DRP;
else if (ContainsDataReductionProxyDefaultHostSuffix(proxy_rules))
rv = PROXY_PREF_CLEARED_GOOGLEZIP;
else
return PROXY_PREF_NOT_CLEARED;
prefs->ClearPref(proxy_config::prefs::kProxy);
return rv;
}
// Before M35, the DRP was configured using a PAC script base64 encoded into a
// PAC url.
if (ProxyModeToString(ProxyPrefs::MODE_PAC_SCRIPT) == mode) {
std::string pac_url;
std::string pac_script;
if (!dict->GetString("pac_url", &pac_url) ||
!GetEmbeddedPacScript(pac_url, &pac_script)) {
return PROXY_PREF_NOT_CLEARED;
}
// In M35 and earlier, the way of specifying the DRP in a PAC script would
// always include the port number after the host even if the port number
// could be implied, so searching for ".googlezip.net:" in the PAC script
// indicates whether there's a proxy in that PAC script with a host of the
// form "*.googlezip.net".
if (pac_script.find(".googlezip.net:") == std::string::npos)
return PROXY_PREF_NOT_CLEARED;
prefs->ClearPref(proxy_config::prefs::kProxy);
return PROXY_PREF_CLEARED_PAC_GOOGLEZIP;
}
return PROXY_PREF_NOT_CLEARED;
}
DataReductionProxyChromeSettings::DataReductionProxyChromeSettings()
: data_reduction_proxy::DataReductionProxySettings(),
data_reduction_proxy_enabled_pref_name_(prefs::kDataSaverEnabled),
profile_(nullptr) {}
DataReductionProxyChromeSettings::~DataReductionProxyChromeSettings() {}
void DataReductionProxyChromeSettings::Shutdown() {
data_reduction_proxy::DataReductionProxyService* service =
data_reduction_proxy_service();
if (service)
service->Shutdown();
}
void DataReductionProxyChromeSettings::InitDataReductionProxySettings(
data_reduction_proxy::DataReductionProxyIOData* io_data,
PrefService* profile_prefs,
net::URLRequestContextGetter* request_context_getter,
Profile* profile,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
std::unique_ptr<data_reduction_proxy::DataStore> store,
const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
const scoped_refptr<base::SequencedTaskRunner>& db_task_runner) {
profile_ = profile;
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
#if defined(OS_ANDROID)
// On mobile we write Data Reduction Proxy prefs directly to the pref service.
// On desktop we store Data Reduction Proxy prefs in memory, writing to disk
// every 60 minutes and on termination. Shutdown hooks must be added for
// Android and iOS in order for non-zero delays to be supported.
// (http://crbug.com/408264)
base::TimeDelta commit_delay = base::TimeDelta();
#else
base::TimeDelta commit_delay = base::TimeDelta::FromMinutes(60);
#endif
if (!data_use_measurement::ChromeDataUseMeasurement::GetInstance()) {
data_use_measurement::ChromeDataUseMeasurement::CreateInstance(
g_browser_process->local_state());
}
std::unique_ptr<data_reduction_proxy::DataReductionProxyService> service =
std::make_unique<data_reduction_proxy::DataReductionProxyService>(
this, profile_prefs, request_context_getter, url_loader_factory,
std::move(store),
std::make_unique<
data_reduction_proxy::DataReductionProxyPingbackClientImpl>(
url_loader_factory, ui_task_runner,
version_info::GetChannelString(chrome::GetChannel())),
g_browser_process->network_quality_tracker(),
content::GetNetworkConnectionTracker(),
data_use_measurement::ChromeDataUseMeasurement::GetInstance(),
ui_task_runner, io_data->io_task_runner(), db_task_runner,
commit_delay);
data_reduction_proxy::DataReductionProxySettings::
InitDataReductionProxySettings(data_reduction_proxy_enabled_pref_name_,
profile_prefs, io_data,
std::move(service));
io_data->SetDataReductionProxyService(
data_reduction_proxy_service()->GetWeakPtr());
data_reduction_proxy::DataReductionProxySettings::
SetCallbackToRegisterSyntheticFieldTrial(base::Bind(
&ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial));
// TODO(bengr): Remove after M46. See http://crbug.com/445599.
MigrateDataReductionProxyOffProxyPrefs(profile_prefs);
}
void DataReductionProxyChromeSettings::SetIgnoreLongTermBlackListRules(
bool ignore_long_term_black_list_rules) {
// |previews_service| is null if |profile_| is off the record.
PreviewsService* previews_service =
PreviewsServiceFactory::GetForProfile(profile_);
if (previews_service && previews_service->previews_ui_service()) {
previews_service->previews_ui_service()
->SetIgnoreLongTermBlackListForServerPreviews(
ignore_long_term_black_list_rules);
}
}
std::unique_ptr<data_reduction_proxy::DataReductionProxyData>
DataReductionProxyChromeSettings::CreateDataFromNavigationHandle(
content::NavigationHandle* handle,
const net::HttpResponseHeaders* headers) {
ChromeNavigationData* chrome_navigation_data =
static_cast<ChromeNavigationData*>(handle->GetNavigationData());
if (chrome_navigation_data) {
if (chrome_navigation_data->GetDataReductionProxyData())
return chrome_navigation_data->GetDataReductionProxyData()->DeepCopy();
return nullptr;
}
// Some unit tests don't have data_reduction_proxy_service() set.
if (!data_reduction_proxy_service())
return nullptr;
// TODO(721403): Need to fill in:
// - client_lofi_requestd_
// - session_key_
// - page_id_
// - request_info_
auto data = std::make_unique<data_reduction_proxy::DataReductionProxyData>();
data->set_request_url(handle->GetURL());
data->set_effective_connection_type(
data_reduction_proxy_service()->GetEffectiveConnectionType());
data->set_connection_type(net::NetworkChangeNotifier::ConnectionType(
data_reduction_proxy_service()->GetConnectionType()));
data->set_used_data_reduction_proxy(
IsConfiguredDataReductionProxy(handle->GetProxyServer()));
if (!headers || headers->IsRedirect(nullptr))
return data;
if (handle->WasResponseCached() &&
headers->HasHeader(data_reduction_proxy::chrome_proxy_header())) {
data->set_was_cached_data_reduction_proxy_response(true);
}
switch (data_reduction_proxy::ParseResponseTransform(*headers)) {
case data_reduction_proxy::TRANSFORM_LITE_PAGE:
data->set_lite_page_received(true);
break;
case data_reduction_proxy::TRANSFORM_PAGE_POLICIES_EMPTY_IMAGE:
data->set_lofi_policy_received(true);
break;
case data_reduction_proxy::TRANSFORM_EMPTY_IMAGE:
data->set_lofi_received(true);
break;
case data_reduction_proxy::TRANSFORM_IDENTITY:
case data_reduction_proxy::TRANSFORM_COMPRESSED_VIDEO:
case data_reduction_proxy::TRANSFORM_NONE:
case data_reduction_proxy::TRANSFORM_UNKNOWN:
break;
}
return data;
}
// static
data_reduction_proxy::Client DataReductionProxyChromeSettings::GetClient() {
#if defined(OS_ANDROID)
return data_reduction_proxy::Client::CHROME_ANDROID;
#elif defined(OS_MACOSX)
return data_reduction_proxy::Client::CHROME_MAC;
#elif defined(OS_CHROMEOS)
return data_reduction_proxy::Client::CHROME_CHROMEOS;
#elif defined(OS_LINUX)
return data_reduction_proxy::Client::CHROME_LINUX;
#elif defined(OS_WIN)
return data_reduction_proxy::Client::CHROME_WINDOWS;
#elif defined(OS_FREEBSD)
return data_reduction_proxy::Client::CHROME_FREEBSD;
#elif defined(OS_OPENBSD)
return data_reduction_proxy::Client::CHROME_OPENBSD;
#elif defined(OS_SOLARIS)
return data_reduction_proxy::Client::CHROME_SOLARIS;
#elif defined(OS_QNX)
return data_reduction_proxy::Client::CHROME_QNX;
#else
return data_reduction_proxy::Client::UNKNOWN;
#endif
}