This source file includes following definitions.
- GetInt64PrefValue
- IsProxyOriginSetOnCommandLine
- IsEnableSpdyProxyAuthSetOnCommandLine
- enabled_by_user_
- InitPrefMembers
- InitDataReductionProxySettings
- InitDataReductionProxySession
- InitDataReductionAuthentication
- AddHostPatternToBypass
- AddURLPatternToBypass
- IsDataReductionProxyAllowed
- IsDataReductionProxyPromoAllowed
- IsPreconnectHintingAllowed
- GetDataReductionProxyOrigin
- GetDataReductionProxyFallback
- IsAcceptableAuthChallenge
- GetTokenForAuthChallenge
- IsDataReductionProxyEnabled
- IsDataReductionProxyManaged
- GetDataReductionProxies
- SetDataReductionProxyEnabled
- GetDataReductionLastUpdateTime
- GetDailyOriginalContentLengths
- GetDailyReceivedContentLengths
- OnURLFetchComplete
- OnIPAddressChanged
- OnProxyEnabledPrefChange
- AddDefaultProxyBypassRules
- LogProxyState
- GetOriginalProfilePrefs
- GetLocalStatePrefs
- ResetDataReductionStatistics
- MaybeActivateDataReductionProxy
- SetProxyConfigs
- RecordDataReductionInit
- RecordProbeURLFetchResult
- RecordStartupState
- GetDailyContentLengths
- GetContentLengths
- GetProxyCheckURL
- AuthHashForSalt
- GetURLFetcher
- ProbeWhetherDataReductionProxyIsAvailable
#include "chrome/browser/net/spdyproxy/data_reduction_proxy_settings.h"
#include <inttypes.h>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_member.h"
#include "base/prefs/pref_service.h"
#include "base/prefs/scoped_user_pref_update.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/prefs/proxy_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "crypto/random.h"
#include "net/base/auth.h"
#include "net/base/host_port_pair.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/http/http_auth.h"
#include "net/http/http_auth_cache.h"
#include "net/http/http_network_session.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_status.h"
#include "url/gurl.h"
using base::FieldTrialList;
using base::StringPrintf;
using spdyproxy::ProbeURLFetchResult;
using spdyproxy::ProxyStartupState;
namespace {
const char kUMAProxyStartupStateHistogram[] =
"DataReductionProxy.StartupState";
const char kUMAProxyProbeURL[] = "DataReductionProxy.ProbeURL";
const char kEnabled[] = "Enabled";
const char kAuthenticationRealmName[] = "SpdyProxy";
int64 GetInt64PrefValue(const base::ListValue& list_value, size_t index) {
int64 val = 0;
std::string pref_value;
bool rv = list_value.GetString(index, &pref_value);
DCHECK(rv);
if (rv) {
rv = base::StringToInt64(pref_value, &val);
DCHECK(rv);
}
return val;
}
bool IsProxyOriginSetOnCommandLine() {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
return command_line.HasSwitch(switches::kSpdyProxyAuthOrigin);
}
bool IsEnableSpdyProxyAuthSetOnCommandLine() {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
return command_line.HasSwitch(switches::kEnableSpdyProxyAuth);
}
}
DataReductionProxySettings::DataReductionProxySettings()
: restricted_by_carrier_(false),
enabled_by_user_(false) {
}
DataReductionProxySettings::~DataReductionProxySettings() {
if (IsDataReductionProxyAllowed())
spdy_proxy_auth_enabled_.Destroy();
}
void DataReductionProxySettings::InitPrefMembers() {
spdy_proxy_auth_enabled_.Init(
prefs::kSpdyProxyAuthEnabled,
GetOriginalProfilePrefs(),
base::Bind(&DataReductionProxySettings::OnProxyEnabledPrefChange,
base::Unretained(this)));
}
void DataReductionProxySettings::InitDataReductionProxySettings() {
InitPrefMembers();
RecordDataReductionInit();
if (!IsDataReductionProxyAllowed())
return;
AddDefaultProxyBypassRules();
net::NetworkChangeNotifier::AddIPAddressObserver(this);
MaybeActivateDataReductionProxy(true);
}
void DataReductionProxySettings::InitDataReductionProxySession(
net::HttpNetworkSession* session) {
#if defined(SPDY_PROXY_AUTH_ORIGIN) && defined(SPDY_PROXY_AUTH_VALUE)
DCHECK(session);
net::HttpAuthCache* auth_cache = session->http_auth_cache();
DCHECK(auth_cache);
InitDataReductionAuthentication(auth_cache);
#endif
}
void DataReductionProxySettings::InitDataReductionAuthentication(
net::HttpAuthCache* auth_cache) {
DCHECK(auth_cache);
int64 timestamp =
(base::Time::Now() - base::Time::UnixEpoch()).InMilliseconds() / 1000;
DataReductionProxyList proxies = GetDataReductionProxies();
for (DataReductionProxyList::iterator it = proxies.begin();
it != proxies.end(); ++it) {
GURL auth_origin = (*it).GetOrigin();
int32 rand[3];
crypto::RandBytes(rand, 3 * sizeof(rand[0]));
std::string realm =
base::StringPrintf("%s%" PRId64, kAuthenticationRealmName, timestamp);
std::string challenge = base::StringPrintf(
"%s realm=\"%s\", ps=\"%" PRId64 "-%u-%u-%u\"",
kAuthenticationRealmName,
realm.data(), timestamp, rand[0], rand[1], rand[2]);
base::string16 password = AuthHashForSalt(timestamp);
DVLOG(1) << "origin: [" << auth_origin << "] realm: [" << realm
<< "] challenge: [" << challenge << "] password: [" << password << "]";
net::AuthCredentials credentials(base::string16(), password);
auth_cache->Add(auth_origin,
realm,
net::HttpAuth::AUTH_SCHEME_SPDYPROXY,
challenge,
credentials,
std::string("/"));
}
}
void DataReductionProxySettings::AddHostPatternToBypass(
const std::string& pattern) {
bypass_rules_.push_back(pattern);
}
void DataReductionProxySettings::AddURLPatternToBypass(
const std::string& pattern) {
size_t pos = pattern.find("/");
if (pattern.find("/", pos + 1) == pos + 1)
pos = pattern.find("/", pos + 2);
std::string host_pattern;
if (pos != std::string::npos)
host_pattern = pattern.substr(0, pos);
else
host_pattern = pattern;
AddHostPatternToBypass(host_pattern);
}
bool DataReductionProxySettings::IsDataReductionProxyAllowed() {
return IsProxyOriginSetOnCommandLine() ||
(FieldTrialList::FindFullName("DataCompressionProxyRollout") == kEnabled);
}
bool DataReductionProxySettings::IsDataReductionProxyPromoAllowed() {
return IsProxyOriginSetOnCommandLine() ||
(IsDataReductionProxyAllowed() &&
FieldTrialList::FindFullName("DataCompressionProxyPromoVisibility") ==
kEnabled);
}
bool DataReductionProxySettings::IsPreconnectHintingAllowed() {
if (!IsDataReductionProxyAllowed())
return false;
return FieldTrialList::FindFullName("DataCompressionProxyPreconnectHints") ==
kEnabled;
}
std::string DataReductionProxySettings::GetDataReductionProxyOrigin() {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kSpdyProxyDevAuthOrigin))
return command_line.GetSwitchValueASCII(switches::kSpdyProxyDevAuthOrigin);
if (command_line.HasSwitch(switches::kSpdyProxyAuthOrigin))
return command_line.GetSwitchValueASCII(switches::kSpdyProxyAuthOrigin);
#if defined(DATA_REDUCTION_DEV_HOST)
if (FieldTrialList::FindFullName("DataCompressionProxyDevRollout") ==
kEnabled) {
return DATA_REDUCTION_DEV_HOST;
}
#endif
#if defined(SPDY_PROXY_AUTH_ORIGIN)
return SPDY_PROXY_AUTH_ORIGIN;
#else
return std::string();
#endif
}
std::string DataReductionProxySettings::GetDataReductionProxyFallback() {
if (GetDataReductionProxyOrigin().empty())
return std::string();
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kSpdyProxyAuthFallback))
return command_line.GetSwitchValueASCII(switches::kSpdyProxyAuthFallback);
#if defined(DATA_REDUCTION_FALLBACK_HOST)
return DATA_REDUCTION_FALLBACK_HOST;
#else
return std::string();
#endif
}
bool DataReductionProxySettings::IsAcceptableAuthChallenge(
net::AuthChallengeInfo* auth_info) {
std::string realm_prefix =
auth_info->realm.substr(0, strlen(kAuthenticationRealmName));
if (realm_prefix != kAuthenticationRealmName)
return false;
DataReductionProxyList proxies = GetDataReductionProxies();
for (DataReductionProxyList::iterator it = proxies.begin();
it != proxies.end(); ++it) {
net::HostPortPair origin_host = net::HostPortPair::FromURL(*it);
if (origin_host.Equals(auth_info->challenger))
return true;
}
return false;
}
base::string16 DataReductionProxySettings::GetTokenForAuthChallenge(
net::AuthChallengeInfo* auth_info) {
if (auth_info->realm.length() > strlen(kAuthenticationRealmName)) {
int64 salt;
std::string realm_suffix =
auth_info->realm.substr(strlen(kAuthenticationRealmName));
if (base::StringToInt64(realm_suffix, &salt)) {
return AuthHashForSalt(salt);
} else {
DVLOG(1) << "Unable to parse realm name " << auth_info->realm
<< "into an int for salting.";
return base::string16();
}
} else {
return base::string16();
}
}
bool DataReductionProxySettings::IsDataReductionProxyEnabled() {
return spdy_proxy_auth_enabled_.GetValue() ||
IsEnableSpdyProxyAuthSetOnCommandLine();
}
bool DataReductionProxySettings::IsDataReductionProxyManaged() {
return spdy_proxy_auth_enabled_.IsManaged();
}
DataReductionProxySettings::DataReductionProxyList
DataReductionProxySettings::GetDataReductionProxies() {
DataReductionProxyList proxies;
std::string proxy = GetDataReductionProxyOrigin();
std::string fallback = GetDataReductionProxyFallback();
if (!proxy.empty())
proxies.push_back(GURL(proxy));
if (!fallback.empty()) {
DCHECK(!proxies.empty());
proxies.push_back(GURL(fallback));
}
return proxies;
}
void DataReductionProxySettings::SetDataReductionProxyEnabled(bool enabled) {
if (!IsDataReductionProxyAllowed())
return;
if (spdy_proxy_auth_enabled_.GetValue() != enabled) {
spdy_proxy_auth_enabled_.SetValue(enabled);
OnProxyEnabledPrefChange();
}
}
int64 DataReductionProxySettings::GetDataReductionLastUpdateTime() {
PrefService* local_state = GetLocalStatePrefs();
int64 last_update_internal =
local_state->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate);
base::Time last_update = base::Time::FromInternalValue(last_update_internal);
return static_cast<int64>(last_update.ToJsTime());
}
DataReductionProxySettings::ContentLengthList
DataReductionProxySettings::GetDailyOriginalContentLengths() {
return GetDailyContentLengths(prefs::kDailyHttpOriginalContentLength);
}
DataReductionProxySettings::ContentLengthList
DataReductionProxySettings::GetDailyReceivedContentLengths() {
return GetDailyContentLengths(prefs::kDailyHttpReceivedContentLength);
}
void DataReductionProxySettings::OnURLFetchComplete(
const net::URLFetcher* source) {
net::URLRequestStatus status = source->GetStatus();
if (status.status() == net::URLRequestStatus::FAILED &&
status.error() == net::ERR_INTERNET_DISCONNECTED) {
RecordProbeURLFetchResult(spdyproxy::INTERNET_DISCONNECTED);
return;
}
std::string response;
source->GetResponseAsString(&response);
if ("OK" == response.substr(0, 2)) {
DVLOG(1) << "The data reduction proxy is unrestricted.";
if (enabled_by_user_) {
if (restricted_by_carrier_) {
SetProxyConfigs(true ,
false ,
false );
RecordProbeURLFetchResult(spdyproxy::SUCCEEDED_PROXY_ENABLED);
} else {
RecordProbeURLFetchResult(spdyproxy::SUCCEEDED_PROXY_ALREADY_ENABLED);
}
}
restricted_by_carrier_ = false;
return;
}
DVLOG(1) << "The data reduction proxy is restricted to the configured "
<< "fallback proxy.";
if (enabled_by_user_) {
if (!restricted_by_carrier_) {
SetProxyConfigs(true ,
true ,
false );
RecordProbeURLFetchResult(spdyproxy::FAILED_PROXY_DISABLED);
} else {
RecordProbeURLFetchResult(spdyproxy::FAILED_PROXY_ALREADY_DISABLED);
}
}
restricted_by_carrier_ = true;
}
void DataReductionProxySettings::OnIPAddressChanged() {
if (enabled_by_user_) {
DCHECK(IsDataReductionProxyAllowed());
ProbeWhetherDataReductionProxyIsAvailable();
}
}
void DataReductionProxySettings::OnProxyEnabledPrefChange() {
if (!DataReductionProxySettings::IsDataReductionProxyAllowed())
return;
MaybeActivateDataReductionProxy(false);
}
void DataReductionProxySettings::AddDefaultProxyBypassRules() {
AddHostPatternToBypass("<local>");
AddHostPatternToBypass("10.0.0.0/8");
AddHostPatternToBypass("172.16.0.0/12");
AddHostPatternToBypass("192.168.0.0/16");
AddHostPatternToBypass("fc00::/7");
AddHostPatternToBypass("*-ds.metric.gstatic.com");
AddHostPatternToBypass("*-v4.metric.gstatic.com");
}
void DataReductionProxySettings::LogProxyState(
bool enabled, bool restricted, bool at_startup) {
const char kAtStartup[] = "at startup";
const char kByUser[] = "by user action";
const char kOn[] = "ON";
const char kOff[] = "OFF";
const char kRestricted[] = "(Restricted)";
const char kUnrestricted[] = "(Unrestricted)";
std::string annotated_on =
kOn + std::string(" ") + (restricted ? kRestricted : kUnrestricted);
LOG(WARNING) << "SPDY proxy " << (enabled ? annotated_on : kOff)
<< " " << (at_startup ? kAtStartup : kByUser);
}
PrefService* DataReductionProxySettings::GetOriginalProfilePrefs() {
return g_browser_process->profile_manager()->GetLastUsedProfile()->
GetOriginalProfile()->GetPrefs();
}
PrefService* DataReductionProxySettings::GetLocalStatePrefs() {
return g_browser_process->local_state();
}
void DataReductionProxySettings::ResetDataReductionStatistics() {
PrefService* prefs = GetLocalStatePrefs();
if (!prefs)
return;
ListPrefUpdate original_update(prefs, prefs::kDailyHttpOriginalContentLength);
ListPrefUpdate received_update(prefs, prefs::kDailyHttpReceivedContentLength);
original_update->Clear();
received_update->Clear();
for (size_t i = 0; i < spdyproxy::kNumDaysInHistory; ++i) {
original_update->AppendString(base::Int64ToString(0));
received_update->AppendString(base::Int64ToString(0));
}
}
void DataReductionProxySettings::MaybeActivateDataReductionProxy(
bool at_startup) {
PrefService* prefs = GetOriginalProfilePrefs();
if (spdy_proxy_auth_enabled_.GetValue() &&
!prefs->GetBoolean(prefs::kSpdyProxyAuthWasEnabledBefore)) {
prefs->SetBoolean(prefs::kSpdyProxyAuthWasEnabledBefore, true);
ResetDataReductionStatistics();
}
std::string proxy = GetDataReductionProxyOrigin();
enabled_by_user_= IsDataReductionProxyEnabled() && !proxy.empty();
SetProxyConfigs(enabled_by_user_, restricted_by_carrier_, at_startup);
if (enabled_by_user_)
ProbeWhetherDataReductionProxyIsAvailable();
}
void DataReductionProxySettings::SetProxyConfigs(
bool enabled, bool restricted, bool at_startup) {
std::string fallback = GetDataReductionProxyFallback();
if (fallback.empty() && enabled && restricted)
enabled = false;
LogProxyState(enabled, restricted, at_startup);
PrefService* prefs = GetOriginalProfilePrefs();
DCHECK(prefs);
DictionaryPrefUpdate update(prefs, prefs::kProxy);
base::DictionaryValue* dict = update.Get();
if (enabled) {
std::string proxy_list;
if (restricted) {
DCHECK(!fallback.empty());
proxy_list = fallback;
} else {
proxy_list = GetDataReductionProxyOrigin() +
(fallback.empty() ? "" : "," + fallback);
}
std::string proxy_server_config = "http=" + proxy_list + ",direct://;";
dict->SetString("server", proxy_server_config);
dict->SetString("mode",
ProxyModeToString(ProxyPrefs::MODE_FIXED_SERVERS));
dict->SetString("bypass_list", JoinString(bypass_rules_, ", "));
} else {
dict->SetString("mode", ProxyModeToString(ProxyPrefs::MODE_SYSTEM));
dict->SetString("server", "");
dict->SetString("bypass_list", "");
}
}
void DataReductionProxySettings::RecordDataReductionInit() {
ProxyStartupState state = spdyproxy::PROXY_NOT_AVAILABLE;
if (IsDataReductionProxyAllowed()) {
if (IsDataReductionProxyEnabled())
state = spdyproxy::PROXY_ENABLED;
else
state = spdyproxy::PROXY_DISABLED;
}
RecordStartupState(state);
}
void DataReductionProxySettings::RecordProbeURLFetchResult(
ProbeURLFetchResult result) {
UMA_HISTOGRAM_ENUMERATION(kUMAProxyProbeURL,
result,
spdyproxy::PROBE_URL_FETCH_RESULT_COUNT);
}
void DataReductionProxySettings::RecordStartupState(ProxyStartupState state) {
UMA_HISTOGRAM_ENUMERATION(kUMAProxyStartupStateHistogram,
state,
spdyproxy::PROXY_STARTUP_STATE_COUNT);
}
DataReductionProxySettings::ContentLengthList
DataReductionProxySettings::GetDailyContentLengths(const char* pref_name) {
DataReductionProxySettings::ContentLengthList content_lengths;
const base::ListValue* list_value = GetLocalStatePrefs()->GetList(pref_name);
if (list_value->GetSize() == spdyproxy::kNumDaysInHistory) {
for (size_t i = 0; i < spdyproxy::kNumDaysInHistory; ++i) {
content_lengths.push_back(GetInt64PrefValue(*list_value, i));
}
}
return content_lengths;
}
void DataReductionProxySettings::GetContentLengths(
unsigned int days,
int64* original_content_length,
int64* received_content_length,
int64* last_update_time) {
DCHECK_LE(days, spdyproxy::kNumDaysInHistory);
PrefService* local_state = GetLocalStatePrefs();
if (!local_state) {
*original_content_length = 0L;
*received_content_length = 0L;
*last_update_time = 0L;
return;
}
const base::ListValue* original_list =
local_state->GetList(prefs::kDailyHttpOriginalContentLength);
const base::ListValue* received_list =
local_state->GetList(prefs::kDailyHttpReceivedContentLength);
if (original_list->GetSize() != spdyproxy::kNumDaysInHistory ||
received_list->GetSize() != spdyproxy::kNumDaysInHistory) {
*original_content_length = 0L;
*received_content_length = 0L;
*last_update_time = 0L;
return;
}
int64 orig = 0L;
int64 recv = 0L;
for (size_t i = spdyproxy::kNumDaysInHistory - days;
i < spdyproxy::kNumDaysInHistory; ++i) {
orig += GetInt64PrefValue(*original_list, i);
recv += GetInt64PrefValue(*received_list, i);
}
*original_content_length = orig;
*received_content_length = recv;
*last_update_time =
local_state->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate);
}
std::string DataReductionProxySettings::GetProxyCheckURL() {
if (!IsDataReductionProxyAllowed())
return std::string();
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kDataReductionProxyProbeURL)) {
return command_line.GetSwitchValueASCII(
switches::kDataReductionProxyProbeURL);
}
#if defined(DATA_REDUCTION_PROXY_PROBE_URL)
return DATA_REDUCTION_PROXY_PROBE_URL;
#else
return std::string();
#endif
}
base::string16 DataReductionProxySettings::AuthHashForSalt(int64 salt) {
if (!IsDataReductionProxyAllowed())
return base::string16();
std::string key;
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kSpdyProxyAuthOrigin)) {
if (!command_line.HasSwitch(switches::kSpdyProxyAuthValue))
return base::string16();
key = command_line.GetSwitchValueASCII(switches::kSpdyProxyAuthValue);
} else {
#if defined(SPDY_PROXY_AUTH_VALUE)
key = SPDY_PROXY_AUTH_VALUE;
#else
return base::string16();
#endif
}
DCHECK(!key.empty());
std::string salted_key =
base::StringPrintf("%" PRId64 "%s%" PRId64, salt, key.c_str(), salt);
return base::UTF8ToUTF16(base::MD5String(salted_key));
}
net::URLFetcher* DataReductionProxySettings::GetURLFetcher() {
std::string url = GetProxyCheckURL();
if (url.empty())
return NULL;
net::URLFetcher* fetcher = net::URLFetcher::Create(GURL(url),
net::URLFetcher::GET,
this);
fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_PROXY);
Profile* profile = ProfileManager::GetActiveUserProfile();
fetcher->SetRequestContext(profile->GetRequestContext());
static const int kMaxRetries = 5;
fetcher->SetMaxRetriesOn5xx(kMaxRetries);
return fetcher;
}
void
DataReductionProxySettings::ProbeWhetherDataReductionProxyIsAvailable() {
net::URLFetcher* fetcher = GetURLFetcher();
if (!fetcher)
return;
fetcher_.reset(fetcher);
fetcher_->Start();
}