blob: a8ed2fefd3a793c2bfba7951877aa399566f2474 [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 "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
#include "components/variations/variations_associated_data.h"
#include "net/base/host_port_pair.h"
#include "net/base/proxy_server.h"
#include "net/http/http_status_code.h"
#include "url/url_constants.h"
#if defined(OS_ANDROID)
#include "base/sys_info.h"
#endif
namespace {
const char kEnabled[] = "Enabled";
const char kControl[] = "Control";
const char kDisabled[] = "Disabled";
const char kDefaultSecureProxyCheckUrl[] = "http://check.googlezip.net/connect";
const char kDefaultWarmupUrl[] = "http://check.googlezip.net/e2e_probe";
const char kQuicFieldTrial[] = "DataReductionProxyUseQuic";
const char kLoFiFieldTrial[] = "DataCompressionProxyLoFi";
const char kLoFiFlagFieldTrial[] = "DataCompressionProxyLoFiFlag";
// Default URL for retrieving the Data Reduction Proxy configuration.
const char kClientConfigURL[] =
"https://datasaver.googleapis.com/v1/clientConfigs";
// Default URL for sending pageload metrics.
const char kPingbackURL[] =
"https://datasaver.googleapis.com/v1/metrics:recordPageloadMetrics";
// The name of the server side experiment field trial.
const char kServerExperimentsFieldTrial[] =
"DataReductionProxyServerExperiments";
// LitePage black list version.
const char kLitePageBlackListVersion[] = "lite-page-blacklist-version";
const char kWarmupFetchCallbackEnabledParam[] = "warmup_fetch_callback_enabled";
const char kMissingViaBypassDisabledParam[] = "bypass_missing_via_disabled";
const char kDiscardCanaryCheckResultParam[] = "store_canary_check_result";
bool IsIncludedInFieldTrial(const std::string& name) {
return base::StartsWith(base::FieldTrialList::FindFullName(name), kEnabled,
base::CompareCase::SENSITIVE);
}
// Returns the variation value for |parameter_name|. If the value is
// unavailable, |default_value| is returned.
std::string GetStringValueForVariationParamWithDefaultValue(
const std::map<std::string, std::string>& variation_params,
const std::string& parameter_name,
const std::string& default_value) {
const auto it = variation_params.find(parameter_name);
if (it == variation_params.end())
return default_value;
return it->second;
}
bool CanShowAndroidLowMemoryDevicePromo() {
#if defined(OS_ANDROID)
return base::SysInfo::IsLowEndDevice() &&
base::FeatureList::IsEnabled(
data_reduction_proxy::features::
kDataReductionProxyLowMemoryDevicePromo);
#endif
return false;
}
} // namespace
namespace data_reduction_proxy {
namespace params {
bool IsIncludedInPromoFieldTrial() {
if (IsIncludedInFieldTrial("DataCompressionProxyPromoVisibility"))
return true;
return CanShowAndroidLowMemoryDevicePromo();
}
bool IsIncludedInFREPromoFieldTrial() {
if (IsIncludedInFieldTrial("DataReductionProxyFREPromo"))
return true;
return CanShowAndroidLowMemoryDevicePromo();
}
bool IsIncludedInHoldbackFieldTrial() {
return IsIncludedInFieldTrial("DataCompressionProxyHoldback");
}
std::string HoldbackFieldTrialGroup() {
return base::FieldTrialList::FindFullName("DataCompressionProxyHoldback");
}
const char* GetLoFiFieldTrialName() {
return kLoFiFieldTrial;
}
const char* GetLoFiFlagFieldTrialName() {
return kLoFiFlagFieldTrial;
}
const char* GetWarmupCallbackParamName() {
return kWarmupFetchCallbackEnabledParam;
}
const char* GetMissingViaBypassParamName() {
return kMissingViaBypassDisabledParam;
}
const char* GetDiscardCanaryCheckResultParam() {
return kDiscardCanaryCheckResultParam;
}
bool ShouldDiscardCanaryCheckResult() {
return GetFieldTrialParamByFeatureAsBool(
features::kDataReductionProxyRobustConnection,
GetDiscardCanaryCheckResultParam(), false);
}
bool IsIncludedInServerExperimentsFieldTrial() {
return !base::CommandLine::ForCurrentProcess()->HasSwitch(
data_reduction_proxy::switches::
kDataReductionProxyServerExperimentsDisabled) &&
base::FieldTrialList::FindFullName(kServerExperimentsFieldTrial)
.find(kDisabled) != 0;
}
bool FetchWarmupProbeURLEnabled() {
return !base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableDataReductionProxyWarmupURLFetch);
}
GURL GetWarmupURL() {
std::map<std::string, std::string> params;
variations::GetVariationParams(GetQuicFieldTrialName(), &params);
return GURL(GetStringValueForVariationParamWithDefaultValue(
params, "warmup_url", kDefaultWarmupUrl));
}
bool IsWhitelistedHttpResponseCodeForProbes(int http_response_code) {
// 200 and 404 are always whitelisted.
if (http_response_code == net::HTTP_OK ||
http_response_code == net::HTTP_NOT_FOUND) {
return true;
}
// Check if there is an additional whitelisted HTTP response code provided via
// the field trial params.
std::map<std::string, std::string> params;
variations::GetVariationParams(GetQuicFieldTrialName(), &params);
const std::string value = GetStringValueForVariationParamWithDefaultValue(
params, "whitelisted_probe_http_response_code", "");
int response_code;
if (!base::StringToInt(value, &response_code))
return false;
return response_code == http_response_code;
}
bool ShouldBypassMissingViaHeader(bool connection_is_cellular) {
return GetFieldTrialParamByFeatureAsBool(
data_reduction_proxy::features::kMissingViaHeaderShortDuration,
connection_is_cellular ? "should_bypass_missing_via_cellular"
: "should_bypass_missing_via_wifi",
true);
}
std::pair<base::TimeDelta, base::TimeDelta>
GetMissingViaHeaderBypassDurationRange(bool connection_is_cellular) {
base::TimeDelta bypass_max =
base::TimeDelta::FromSeconds(GetFieldTrialParamByFeatureAsInt(
data_reduction_proxy::features::kMissingViaHeaderShortDuration,
connection_is_cellular ? "missing_via_max_bypass_cellular_in_seconds"
: "missing_via_max_bypass_wifi_in_seconds",
300));
base::TimeDelta bypass_min =
base::TimeDelta::FromSeconds(GetFieldTrialParamByFeatureAsInt(
data_reduction_proxy::features::kMissingViaHeaderShortDuration,
connection_is_cellular ? "missing_via_min_bypass_cellular_in_seconds"
: "missing_via_min_bypass_wifi_in_seconds",
60));
return {bypass_min, bypass_max};
}
bool IsForcePingbackEnabledViaFlags() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
data_reduction_proxy::switches::kEnableDataReductionProxyForcePingback);
}
bool WarnIfNoDataReductionProxy() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
data_reduction_proxy::switches::kEnableDataReductionProxyBypassWarning);
}
bool IsIncludedInQuicFieldTrial() {
if (base::StartsWith(base::FieldTrialList::FindFullName(kQuicFieldTrial),
kControl, base::CompareCase::SENSITIVE)) {
return false;
}
if (base::StartsWith(base::FieldTrialList::FindFullName(kQuicFieldTrial),
kDisabled, base::CompareCase::SENSITIVE)) {
return false;
}
// QUIC is enabled by default.
return true;
}
bool IsQuicEnabledForNonCoreProxies() {
DCHECK(IsIncludedInQuicFieldTrial());
std::map<std::string, std::string> params;
variations::GetVariationParams(GetQuicFieldTrialName(), &params);
return GetStringValueForVariationParamWithDefaultValue(
params, "enable_quic_non_core_proxies", "true") != "false";
}
const char* GetQuicFieldTrialName() {
return kQuicFieldTrial;
}
bool IsBrotliAcceptEncodingEnabled() {
// Brotli encoding is enabled by default since the data reduction proxy server
// controls when to serve Brotli encoded content. It can be disabled in
// Chromium only if Chromium belongs to a field trial group whose name starts
// with "Disabled".
return !base::StartsWith(base::FieldTrialList::FindFullName(
"DataReductionProxyBrotliAcceptEncoding"),
kDisabled, base::CompareCase::SENSITIVE);
}
bool IsConfigClientEnabled() {
// Config client is enabled by default. It can be disabled only if Chromium
// belongs to a field trial group whose name starts with "Disabled".
return !base::StartsWith(
base::FieldTrialList::FindFullName("DataReductionProxyConfigService"),
kDisabled, base::CompareCase::SENSITIVE);
}
GURL GetConfigServiceURL() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
std::string url;
if (command_line->HasSwitch(switches::kDataReductionProxyConfigURL)) {
url = command_line->GetSwitchValueASCII(
switches::kDataReductionProxyConfigURL);
}
if (url.empty())
return GURL(kClientConfigURL);
GURL result(url);
if (result.is_valid())
return result;
LOG(WARNING) << "The following client config URL specified at the "
<< "command-line or variation is invalid: " << url;
return GURL(kClientConfigURL);
}
GURL GetPingbackURL() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
std::string url;
if (command_line->HasSwitch(switches::kDataReductionPingbackURL)) {
url =
command_line->GetSwitchValueASCII(switches::kDataReductionPingbackURL);
}
if (url.empty())
return GURL(kPingbackURL);
GURL result(url);
if (result.is_valid())
return result;
LOG(WARNING) << "The following page load metrics URL specified at the "
<< "command-line or variation is invalid: " << url;
return GURL(kPingbackURL);
}
bool ShouldForceEnableDataReductionProxy() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
data_reduction_proxy::switches::kEnableDataReductionProxy);
}
int LitePageVersion() {
return GetFieldTrialParameterAsInteger(
data_reduction_proxy::params::GetLoFiFieldTrialName(),
kLitePageBlackListVersion, 0, 0);
}
int GetFieldTrialParameterAsInteger(const std::string& group,
const std::string& param_name,
int default_value,
int min_value) {
DCHECK(default_value >= min_value);
std::string param_value =
variations::GetVariationParamValue(group, param_name);
int value;
if (param_value.empty() || !base::StringToInt(param_value, &value) ||
value < min_value) {
return default_value;
}
return value;
}
bool GetOverrideProxiesForHttpFromCommandLine(
std::vector<DataReductionProxyServer>* override_proxies_for_http) {
DCHECK(override_proxies_for_http);
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDataReductionProxyHttpProxies)) {
// It is illegal to use |kDataReductionProxy| or
// |kDataReductionProxyFallback| with |kDataReductionProxyHttpProxies|.
DCHECK(base::CommandLine::ForCurrentProcess()
->GetSwitchValueASCII(switches::kDataReductionProxy)
.empty());
DCHECK(base::CommandLine::ForCurrentProcess()
->GetSwitchValueASCII(switches::kDataReductionProxyFallback)
.empty());
override_proxies_for_http->clear();
std::string proxy_overrides =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kDataReductionProxyHttpProxies);
for (const auto& proxy_override :
base::SplitStringPiece(proxy_overrides, ";", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY)) {
net::ProxyServer proxy_server = net::ProxyServer::FromURI(
proxy_override, net::ProxyServer::SCHEME_HTTP);
DCHECK(proxy_server.is_valid());
DCHECK(!proxy_server.is_direct());
// Overriding proxies have type UNSPECIFIED_TYPE.
override_proxies_for_http->push_back(DataReductionProxyServer(
std::move(proxy_server), ProxyServer::UNSPECIFIED_TYPE));
}
return true;
}
std::string origin =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kDataReductionProxy);
std::string fallback_origin =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kDataReductionProxyFallback);
if (origin.empty() && fallback_origin.empty())
return false;
override_proxies_for_http->clear();
// Overriding proxies have type UNSPECIFIED_TYPE.
if (!origin.empty()) {
net::ProxyServer primary_proxy =
net::ProxyServer::FromURI(origin, net::ProxyServer::SCHEME_HTTP);
DCHECK(primary_proxy.is_valid());
DCHECK(!primary_proxy.is_direct());
override_proxies_for_http->push_back(DataReductionProxyServer(
std::move(primary_proxy), ProxyServer::UNSPECIFIED_TYPE));
}
if (!fallback_origin.empty()) {
net::ProxyServer fallback_proxy = net::ProxyServer::FromURI(
fallback_origin, net::ProxyServer::SCHEME_HTTP);
DCHECK(fallback_proxy.is_valid());
DCHECK(!fallback_proxy.is_direct());
override_proxies_for_http->push_back(DataReductionProxyServer(
std::move(fallback_proxy), ProxyServer::UNSPECIFIED_TYPE));
}
return true;
}
const char* GetServerExperimentsFieldTrialName() {
return kServerExperimentsFieldTrial;
}
GURL GetSecureProxyCheckURL() {
std::string secure_proxy_check_url =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kDataReductionProxySecureProxyCheckURL);
if (secure_proxy_check_url.empty())
secure_proxy_check_url = kDefaultSecureProxyCheckUrl;
return GURL(secure_proxy_check_url);
}
bool IsDataSaverSiteBreakdownUsingPLMEnabled() {
return base::FeatureList::IsEnabled(
data_reduction_proxy::features::
kDataSaverSiteBreakdownUsingPageLoadMetrics);
}
} // namespace params
DataReductionProxyParams::DataReductionProxyParams() {
bool use_override_proxies_for_http =
params::GetOverrideProxiesForHttpFromCommandLine(&proxies_for_http_);
if (!use_override_proxies_for_http) {
DCHECK(proxies_for_http_.empty());
proxies_for_http_.push_back(DataReductionProxyServer(
net::ProxyServer::FromURI("https://proxy.googlezip.net:443",
net::ProxyServer::SCHEME_HTTP),
ProxyServer::CORE));
proxies_for_http_.push_back(DataReductionProxyServer(
net::ProxyServer::FromURI("compress.googlezip.net:80",
net::ProxyServer::SCHEME_HTTP),
ProxyServer::CORE));
}
DCHECK(std::all_of(proxies_for_http_.begin(), proxies_for_http_.end(),
[](const DataReductionProxyServer& proxy) {
return proxy.proxy_server().is_valid() &&
!proxy.proxy_server().is_direct();
}));
}
DataReductionProxyParams::~DataReductionProxyParams() {}
void DataReductionProxyParams::SetProxiesForHttpForTesting(
const std::vector<DataReductionProxyServer>& proxies_for_http) {
DCHECK(std::all_of(proxies_for_http.begin(), proxies_for_http.end(),
[](const DataReductionProxyServer& proxy) {
return proxy.proxy_server().is_valid() &&
!proxy.proxy_server().is_direct();
}));
proxies_for_http_ = proxies_for_http;
}
const std::vector<DataReductionProxyServer>&
DataReductionProxyParams::proxies_for_http() const {
return proxies_for_http_;
}
base::Optional<DataReductionProxyTypeInfo>
DataReductionProxyParams::FindConfiguredDataReductionProxy(
const net::ProxyServer& proxy_server) const {
return FindConfiguredProxyInVector(proxies_for_http(), proxy_server);
}
// static
base::Optional<DataReductionProxyTypeInfo>
DataReductionProxyParams::FindConfiguredProxyInVector(
const std::vector<DataReductionProxyServer>& proxies,
const net::ProxyServer& proxy_server) {
if (!proxy_server.is_valid() || proxy_server.is_direct())
return base::nullopt;
// Only compare the host port pair of the |proxy_server| since the proxy
// scheme of the stored data reduction proxy may be different than the proxy
// scheme of |proxy_server|. This may happen even when the |proxy_server| is a
// valid data reduction proxy. As an example, the stored data reduction proxy
// may have a proxy scheme of HTTPS while |proxy_server| may have QUIC as the
// proxy scheme.
const net::HostPortPair& host_port_pair = proxy_server.host_port_pair();
auto it = std::find_if(
proxies.begin(), proxies.end(),
[&host_port_pair](const DataReductionProxyServer& proxy) {
return proxy.proxy_server().host_port_pair().Equals(host_port_pair);
});
if (it == proxies.end())
return base::nullopt;
return DataReductionProxyTypeInfo(proxies,
static_cast<size_t>(it - proxies.begin()));
}
} // namespace data_reduction_proxy