Commit aae544d7 authored by fgorski@chromium.org's avatar fgorski@chromium.org

G-services settings v3 implementation

G-services settings v3 implementation:
* Adding calculation of settings based on a diff from server
* Adding calculation of settings digest
* Changing internal representation of settings to string->string map
  in order to be able to calculate the digest properly.
* Individual settings are calculated when needed.

R=jianli@chromium.org,zea@chromium.org
BUG=359256

Review URL: https://codereview.chromium.org/288433002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271600 0039d316-1c4b-4281-b951-d872f2087c98
parent 65382427
......@@ -17,7 +17,7 @@ namespace gcm {
namespace {
const char kRequestContentType[] = "application/x-protobuf";
const int kRequestVersionValue = 2;
const int kRequestVersionValue = 3;
const int kDefaultUserSerialNumber = 0;
// This enum is also used in an UMA histogram (GCMCheckinRequestStatus
......
......@@ -5,7 +5,9 @@
#include "google_apis/gcm/engine/gservices_settings.h"
#include "base/bind.h"
#include "base/sha1.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
namespace {
......@@ -28,12 +30,142 @@ const int kDefaultMCSMainSecurePort = 5228;
const int kDefaultMCSFallbackSecurePort = 443;
const char kDefaultRegistrationURL[] =
"https://android.clients.google.com/c2dm/register3";
// Settings that are to be deleted are marked with this prefix in checkin
// response.
const char kDeleteSettingPrefix[] = "delete_";
// Settings digest starts with verison number followed by '-'.
const char kDigestVersionPrefix[] = "1-";
const char kMCSEnpointTemplate[] = "https://%s:%d";
const int kMaxSecurePort = 65535;
std::string MakeMCSEndpoint(const std::string& mcs_hostname, int port) {
return base::StringPrintf(kMCSEnpointTemplate, mcs_hostname.c_str(), port);
}
// Default settings can be omitted, as GServicesSettings class provides
// reasonable defaults.
bool CanBeOmitted(const std::string& settings_name) {
return settings_name == kCheckinIntervalKey ||
settings_name == kCheckinURLKey ||
settings_name == kMCSHostnameKey ||
settings_name == kMCSSecurePortKey ||
settings_name == kRegistrationURLKey;
}
bool VerifyCheckinInterval(
const gcm::GServicesSettings::SettingsMap& settings) {
gcm::GServicesSettings::SettingsMap::const_iterator iter =
settings.find(kCheckinIntervalKey);
if (iter == settings.end())
return CanBeOmitted(kCheckinIntervalKey);
int64 checkin_interval = kMinimumCheckinInterval;
if (!base::StringToInt64(iter->second, &checkin_interval)) {
DVLOG(1) << "Failed to parse checkin interval: " << iter->second;
return false;
}
if (checkin_interval == std::numeric_limits<int64>::max()) {
DVLOG(1) << "Checkin interval is too big: " << checkin_interval;
return false;
}
if (checkin_interval < kMinimumCheckinInterval) {
DVLOG(1) << "Checkin interval: " << checkin_interval
<< " is less than allowed minimum: " << kMinimumCheckinInterval;
}
return true;
}
bool VerifyMCSEndpoint(const gcm::GServicesSettings::SettingsMap& settings) {
std::string mcs_hostname;
gcm::GServicesSettings::SettingsMap::const_iterator iter =
settings.find(kMCSHostnameKey);
if (iter == settings.end()) {
// Because endpoint has 2 parts (hostname and port) we are defaulting and
// moving on with verification.
if (CanBeOmitted(kMCSHostnameKey))
mcs_hostname = kDefaultMCSHostname;
else
return false;
} else if (iter->second.empty()) {
DVLOG(1) << "Empty MCS hostname provided.";
return false;
} else {
mcs_hostname = iter->second;
}
int mcs_secure_port = 0;
iter = settings.find(kMCSSecurePortKey);
if (iter == settings.end()) {
// Simlarly we might have to default the port, when only hostname is
// provided.
if (CanBeOmitted(kMCSSecurePortKey))
mcs_secure_port = kDefaultMCSMainSecurePort;
else
return false;
} else if (!base::StringToInt(iter->second, &mcs_secure_port)) {
DVLOG(1) << "Failed to parse MCS secure port: " << iter->second;
return false;
}
if (mcs_secure_port < 0 || mcs_secure_port > kMaxSecurePort) {
DVLOG(1) << "Incorrect port value: " << mcs_secure_port;
return false;
}
GURL mcs_main_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
if (!mcs_main_endpoint.is_valid()) {
DVLOG(1) << "Invalid main MCS endpoint: "
<< mcs_main_endpoint.possibly_invalid_spec();
return false;
}
GURL mcs_fallback_endpoint(
MakeMCSEndpoint(mcs_hostname, kDefaultMCSFallbackSecurePort));
if (!mcs_fallback_endpoint.is_valid()) {
DVLOG(1) << "Invalid fallback MCS endpoint: "
<< mcs_fallback_endpoint.possibly_invalid_spec();
return false;
}
return true;
}
bool VerifyCheckinURL(const gcm::GServicesSettings::SettingsMap& settings) {
gcm::GServicesSettings::SettingsMap::const_iterator iter =
settings.find(kCheckinURLKey);
if (iter == settings.end())
return CanBeOmitted(kCheckinURLKey);
GURL checkin_url(iter->second);
if (!checkin_url.is_valid()) {
DVLOG(1) << "Invalid checkin URL provided: " << iter->second;
return false;
}
return true;
}
bool VerifyRegistrationURL(
const gcm::GServicesSettings::SettingsMap& settings) {
gcm::GServicesSettings::SettingsMap::const_iterator iter =
settings.find(kRegistrationURLKey);
if (iter == settings.end())
return CanBeOmitted(kRegistrationURLKey);
GURL registration_url(iter->second);
if (!registration_url.is_valid()) {
DVLOG(1) << "Invalid registration URL provided: " << iter->second;
return false;
}
return true;
}
bool VerifySettings(const gcm::GServicesSettings::SettingsMap& settings) {
return VerifyCheckinInterval(settings) && VerifyMCSEndpoint(settings) &&
VerifyCheckinURL(settings) && VerifyRegistrationURL(settings);
}
} // namespace
namespace gcm {
......@@ -48,152 +180,163 @@ const GURL GServicesSettings::DefaultCheckinURL() {
return GURL(kDefaultCheckinURL);
}
GServicesSettings::GServicesSettings()
: checkin_interval_(base::TimeDelta::FromSeconds(kDefaultCheckinInterval)),
checkin_url_(kDefaultCheckinURL),
mcs_main_endpoint_(MakeMCSEndpoint(kDefaultMCSHostname,
kDefaultMCSMainSecurePort)),
mcs_fallback_endpoint_(MakeMCSEndpoint(kDefaultMCSHostname,
kDefaultMCSFallbackSecurePort)),
registration_url_(kDefaultRegistrationURL),
weak_ptr_factory_(this) {
// static
std::string GServicesSettings::CalculateDigest(const SettingsMap& settings) {
unsigned char hash[base::kSHA1Length];
std::string data;
for (SettingsMap::const_iterator iter = settings.begin();
iter != settings.end();
++iter) {
data += iter->first;
data += '\0';
data += iter->second;
data += '\0';
}
base::SHA1HashBytes(
reinterpret_cast<const unsigned char*>(&data[0]), data.size(), hash);
std::string digest =
kDigestVersionPrefix + base::HexEncode(hash, base::kSHA1Length);
digest = StringToLowerASCII(digest);
return digest;
}
GServicesSettings::GServicesSettings() : weak_ptr_factory_(this) {
digest_ = CalculateDigest(settings_);
}
GServicesSettings::~GServicesSettings() {}
GServicesSettings::~GServicesSettings() {
}
bool GServicesSettings::UpdateFromCheckinResponse(
const checkin_proto::AndroidCheckinResponse& checkin_response) {
if (!checkin_response.has_digest() ||
checkin_response.digest() == digest_) {
// There are no changes as digest is the same or no settings provided.
const checkin_proto::AndroidCheckinResponse& checkin_response) {
if (!checkin_response.has_settings_diff()) {
DVLOG(1) << "Field settings_diff not set in response.";
return false;
}
std::map<std::string, std::string> settings;
bool settings_diff = checkin_response.settings_diff();
SettingsMap new_settings;
// Only reuse the existing settings, if we are given a settings difference.
if (settings_diff)
new_settings = settings_map();
for (int i = 0; i < checkin_response.setting_size(); ++i) {
std::string name = checkin_response.setting(i).name();
std::string value = checkin_response.setting(i).value();
settings[name] = value;
}
if (name.empty()) {
DVLOG(1) << "Setting name is empty";
return false;
}
// Only update the settings in store and digest, if the settings actually
// passed the verificaiton in update settings.
if (UpdateSettings(settings)) {
digest_ = checkin_response.digest();
return true;
if (settings_diff && name.find(kDeleteSettingPrefix) == 0) {
std::string setting_to_delete =
name.substr(arraysize(kDeleteSettingPrefix) - 1);
new_settings.erase(setting_to_delete);
DVLOG(1) << "Setting deleted: " << setting_to_delete;
} else {
std::string value = checkin_response.setting(i).value();
new_settings[name] = value;
DVLOG(1) << "New setting: '" << name << "' : '" << value << "'";
}
}
return false;
if (!VerifySettings(new_settings))
return false;
settings_.swap(new_settings);
digest_ = CalculateDigest(settings_);
return true;
}
void GServicesSettings::UpdateFromLoadResult(
const GCMStore::LoadResult& load_result) {
if (UpdateSettings(load_result.gservices_settings))
digest_ = load_result.gservices_digest;
}
// No need to try to update settings when load_result is empty.
if (load_result.gservices_settings.empty())
return;
if (!VerifySettings(load_result.gservices_settings))
return;
std::string digest = CalculateDigest(load_result.gservices_settings);
if (digest != load_result.gservices_digest) {
DVLOG(1) << "G-services settings digest mismatch. "
<< "Expected digest: " << load_result.gservices_digest
<< ". Calculated digest is: " << digest;
return;
}
std::map<std::string, std::string> GServicesSettings::GetSettingsMap() const {
std::map<std::string, std::string> settings;
settings[kCheckinIntervalKey] =
base::Int64ToString(checkin_interval_.InSeconds());
settings[kCheckinURLKey] = checkin_url_.spec();
settings[kMCSHostnameKey] = mcs_main_endpoint_.host();
settings[kMCSSecurePortKey] = mcs_main_endpoint_.port();
settings[kRegistrationURLKey] = registration_url_.spec();
return settings;
settings_ = load_result.gservices_settings;
digest_ = load_result.gservices_digest;
}
bool GServicesSettings::UpdateSettings(
const std::map<std::string, std::string>& settings) {
int64 new_checkin_interval = kMinimumCheckinInterval;
std::map<std::string, std::string>::const_iterator iter =
settings.find(kCheckinIntervalKey);
if (iter == settings.end()) {
LOG(ERROR) << "Setting not found: " << kCheckinIntervalKey;
return false;
}
if (!base::StringToInt64(iter->second, &new_checkin_interval)) {
LOG(ERROR) << "Failed to parse checkin interval: " << iter->second;
return false;
}
if (new_checkin_interval < kMinimumCheckinInterval) {
LOG(ERROR) << "Checkin interval: " << new_checkin_interval
<< " is less than allowed minimum: " << kMinimumCheckinInterval;
new_checkin_interval = kMinimumCheckinInterval;
}
if (new_checkin_interval == std::numeric_limits<int64>::max()) {
LOG(ERROR) << "Checkin interval is too big: " << new_checkin_interval;
return false;
base::TimeDelta GServicesSettings::GetCheckinInterval() const {
int64 checkin_interval = kMinimumCheckinInterval;
SettingsMap::const_iterator iter = settings_.find(kCheckinIntervalKey);
if (iter == settings_.end() ||
!base::StringToInt64(iter->second, &checkin_interval)) {
checkin_interval = kDefaultCheckinInterval;
}
std::string new_mcs_hostname;
iter = settings.find(kMCSHostnameKey);
if (iter == settings.end()) {
LOG(ERROR) << "Setting not found: " << kMCSHostnameKey;
return false;
}
new_mcs_hostname = iter->second;
if (new_mcs_hostname.empty()) {
LOG(ERROR) << "Empty MCS hostname provided.";
return false;
}
if (checkin_interval < kMinimumCheckinInterval)
checkin_interval = kMinimumCheckinInterval;
int new_mcs_secure_port = -1;
iter = settings.find(kMCSSecurePortKey);
if (iter == settings.end()) {
LOG(ERROR) << "Setting not found: " << kMCSSecurePortKey;
return false;
}
if (!base::StringToInt(iter->second, &new_mcs_secure_port)) {
LOG(ERROR) << "Failed to parse MCS secure port: " << iter->second;
return false;
}
if (new_mcs_secure_port < 0 || 65535 < new_mcs_secure_port) {
LOG(ERROR) << "Incorrect port value: " << new_mcs_secure_port;
return false;
}
return base::TimeDelta::FromSeconds(checkin_interval);
}
GURL new_mcs_main_endpoint =
GURL(MakeMCSEndpoint(new_mcs_hostname, new_mcs_secure_port));
GURL new_mcs_fallback_endpoint =
GURL(MakeMCSEndpoint(new_mcs_hostname, kDefaultMCSFallbackSecurePort));
if (!new_mcs_main_endpoint.is_valid() ||
!new_mcs_fallback_endpoint.is_valid())
return false;
GURL GServicesSettings::GetCheckinURL() const {
SettingsMap::const_iterator iter = settings_.find(kCheckinURLKey);
if (iter == settings_.end() || iter->second.empty())
return GURL(kDefaultCheckinURL);
return GURL(iter->second);
}
GURL new_checkin_url;
iter = settings.find(kCheckinURLKey);
if (iter == settings.end()) {
LOG(ERROR) << "Setting not found: " << kCheckinURLKey;
return false;
}
new_checkin_url = GURL(iter->second);
if (!new_checkin_url.is_valid()) {
LOG(ERROR) << "Invalid checkin URL provided: "
<< new_checkin_url.possibly_invalid_spec();
return false;
}
GURL GServicesSettings::GetMCSMainEndpoint() const {
// Get alternative hostname or use default.
std::string mcs_hostname;
SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
if (iter != settings_.end() && !iter->second.empty())
mcs_hostname = iter->second;
else
mcs_hostname = kDefaultMCSHostname;
GURL new_registration_url;
iter = settings.find(kRegistrationURLKey);
if (iter == settings.end()) {
LOG(ERROR) << "Setting not found: " << kRegistrationURLKey;
return false;
}
new_registration_url = GURL(iter->second);
if (!new_registration_url.is_valid()) {
LOG(ERROR) << "Invalid registration URL provided: "
<< new_registration_url.possibly_invalid_spec();
return false;
// Get alternative secure port or use defualt.
int mcs_secure_port = 0;
iter = settings_.find(kMCSSecurePortKey);
if (iter == settings_.end() || iter->second.empty() ||
!base::StringToInt(iter->second, &mcs_secure_port)) {
mcs_secure_port = kDefaultMCSMainSecurePort;
}
// We only update the settings once all of them are correct.
checkin_interval_ = base::TimeDelta::FromSeconds(new_checkin_interval);
mcs_main_endpoint_ = new_mcs_main_endpoint;
mcs_fallback_endpoint_ = new_mcs_fallback_endpoint;
checkin_url_ = new_checkin_url;
registration_url_ = new_registration_url;
return true;
// If constructed address makes sense use it.
GURL mcs_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
if (mcs_endpoint.is_valid())
return mcs_endpoint;
// Otherwise use default settings.
return GURL(MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSMainSecurePort));
}
GURL GServicesSettings::GetMCSFallbackEndpoint() const {
// Get alternative hostname or use default.
std::string mcs_hostname;
SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
if (iter != settings_.end() && !iter->second.empty())
mcs_hostname = iter->second;
else
mcs_hostname = kDefaultMCSHostname;
// If constructed address makes sense use it.
GURL mcs_endpoint(
MakeMCSEndpoint(mcs_hostname, kDefaultMCSFallbackSecurePort));
if (mcs_endpoint.is_valid())
return mcs_endpoint;
return GURL(
MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSFallbackSecurePort));
}
GURL GServicesSettings::GetRegistrationURL() const {
SettingsMap::const_iterator iter = settings_.find(kRegistrationURLKey);
if (iter == settings_.end() || iter->second.empty())
return GURL(kDefaultRegistrationURL);
return GURL(iter->second);
}
} // namespace gcm
......@@ -21,12 +21,17 @@ namespace gcm {
// extracting them from checkin response and storing in GCMStore.
class GCM_EXPORT GServicesSettings {
public:
typedef std::map<std::string, std::string> SettingsMap;
// Minimum periodic checkin interval in seconds.
static const base::TimeDelta MinimumCheckinInterval();
// Default checkin URL.
static const GURL DefaultCheckinURL();
// Calculates digest of provided settings.
static std::string CalculateDigest(const SettingsMap& settings);
GServicesSettings();
~GServicesSettings();
......@@ -38,45 +43,33 @@ class GCM_EXPORT GServicesSettings {
// successful, false otherwise.
void UpdateFromLoadResult(const GCMStore::LoadResult& load_result);
// Gets the settings as a map of string to string for storing.
std::map<std::string, std::string> GetSettingsMap() const;
SettingsMap settings_map() const { return settings_; }
std::string digest() const { return digest_; }
base::TimeDelta checkin_interval() const { return checkin_interval_; }
// Gets the interval at which device should perform a checkin.
base::TimeDelta GetCheckinInterval() const;
GURL checkin_url() const { return checkin_url_; }
// Gets the URL to use when checking in.
GURL GetCheckinURL() const;
GURL mcs_main_endpoint() const { return mcs_main_endpoint_; }
// Gets address of main MCS endpoint.
GURL GetMCSMainEndpoint() const;
GURL mcs_fallback_endpoint() const { return mcs_fallback_endpoint_; }
// Gets address of fallback MCS endpoint.
GURL GetMCSFallbackEndpoint() const;
GURL registration_url() const { return registration_url_; }
// Gets the URL to use when registering or unregistering the apps.
GURL GetRegistrationURL() const;
private:
// Parses the |settings| to fill in specific fields.
// TODO(fgorski): Change to a status variable that can be logged to UMA.
bool UpdateSettings(const std::map<std::string, std::string>& settings);
// Digest (hash) of the settings, used to check whether settings need update.
// It is meant to be sent with checkin request, instead of sending the whole
// settings table.
std::string digest_;
// Time delta between periodic checkins.
base::TimeDelta checkin_interval_;
// URL that should be used for checkins.
GURL checkin_url_;
// Main MCS endpoint.
GURL mcs_main_endpoint_;
// Fallback MCS endpoint.
GURL mcs_fallback_endpoint_;
// URL that should be used for regisrations and unregistrations.
GURL registration_url_;
// G-services settings as provided by checkin response.
SettingsMap settings_;
// Factory for creating references in callbacks.
base::WeakPtrFactory<GServicesSettings> weak_ptr_factory_;
......
......@@ -23,6 +23,24 @@ const int64 kDefaultCheckinInterval = 2 * 24 * 60 * 60; // seconds = 2 days.
const char kDefaultCheckinURL[] = "https://android.clients.google.com/checkin";
const char kDefaultRegistrationURL[] =
"https://android.clients.google.com/c2dm/register3";
const char kDefaultSettingsDigest[] =
"1-da39a3ee5e6b4b0d3255bfef95601890afd80709";
const char kAlternativeSettingsDigest[] =
"1-7da4aa4eb38a8bd3e330e3751cc0899924499134";
void AddSettingsToResponse(
checkin_proto::AndroidCheckinResponse& checkin_response,
const GServicesSettings::SettingsMap& settings,
bool settings_diff) {
for (GServicesSettings::SettingsMap::const_iterator iter = settings.begin();
iter != settings.end();
++iter) {
checkin_proto::GservicesSetting* setting = checkin_response.add_setting();
setting->set_name(iter->first);
setting->set_value(iter->second);
}
checkin_response.set_settings_diff(settings_diff);
}
} // namespace
......@@ -31,23 +49,12 @@ class GServicesSettingsTest : public testing::Test {
GServicesSettingsTest();
virtual ~GServicesSettingsTest();
virtual void SetUp() OVERRIDE;
void CheckAllSetToDefault();
void CheckAllSetToAlternative();
void SetWithAlternativeSettings(
checkin_proto::AndroidCheckinResponse& checkin_response);
GServicesSettings& settings() {
return gserivces_settings_;
}
const std::map<std::string, std::string>& alternative_settings() {
return alternative_settings_;
}
std::map<std::string, std::string> alternative_settings_;
private:
GServicesSettings gserivces_settings_;
};
......@@ -58,159 +65,273 @@ GServicesSettingsTest::GServicesSettingsTest()
GServicesSettingsTest::~GServicesSettingsTest() {}
void GServicesSettingsTest::SetUp() {
alternative_settings_["checkin_interval"] =
base::Int64ToString(kAlternativeCheckinInterval);
alternative_settings_["checkin_url"] = kAlternativeCheckinURL;
alternative_settings_["gcm_hostname"] = kAlternativeMCSHostname;
alternative_settings_["gcm_secure_port"] =
base::IntToString(kAlternativeMCSSecurePort);
alternative_settings_["gcm_registration_url"] = kAlternativeRegistrationURL;
}
void GServicesSettingsTest::CheckAllSetToDefault() {
EXPECT_EQ(base::TimeDelta::FromSeconds(kDefaultCheckinInterval),
settings().checkin_interval());
EXPECT_EQ(GURL(kDefaultCheckinURL), settings().checkin_url());
settings().GetCheckinInterval());
EXPECT_EQ(GURL(kDefaultCheckinURL), settings().GetCheckinURL());
EXPECT_EQ(GURL("https://mtalk.google.com:5228"),
settings().mcs_main_endpoint());
settings().GetMCSMainEndpoint());
EXPECT_EQ(GURL("https://mtalk.google.com:443"),
settings().mcs_fallback_endpoint());
EXPECT_EQ(GURL(kDefaultRegistrationURL), settings().registration_url());
}
void GServicesSettingsTest::CheckAllSetToAlternative() {
EXPECT_EQ(base::TimeDelta::FromSeconds(kAlternativeCheckinInterval),
settings().checkin_interval());
EXPECT_EQ(GURL(kAlternativeCheckinURL), settings().checkin_url());
EXPECT_EQ(GURL("https://alternative.gcm.host:7777"),
settings().mcs_main_endpoint());
EXPECT_EQ(GURL("https://alternative.gcm.host:443"),
settings().mcs_fallback_endpoint());
EXPECT_EQ(GURL(kAlternativeRegistrationURL), settings().registration_url());
}
void GServicesSettingsTest::SetWithAlternativeSettings(
checkin_proto::AndroidCheckinResponse& checkin_response) {
for (std::map<std::string, std::string>::const_iterator iter =
alternative_settings_.begin();
iter != alternative_settings_.end(); ++iter) {
checkin_proto::GservicesSetting* setting = checkin_response.add_setting();
setting->set_name(iter->first);
setting->set_value(iter->second);
}
settings().GetMCSFallbackEndpoint());
EXPECT_EQ(GURL(kDefaultRegistrationURL), settings().GetRegistrationURL());
}
// Verifies default values of the G-services settings and settings digest.
TEST_F(GServicesSettingsTest, DefaultSettingsAndDigest) {
CheckAllSetToDefault();
EXPECT_EQ(std::string(), settings().digest());
EXPECT_EQ(kDefaultSettingsDigest, settings().digest());
EXPECT_EQ(kDefaultSettingsDigest,
GServicesSettings::CalculateDigest(settings().settings_map()));
}
// Verifies digest calculation for the sample provided by protocol owners.
TEST_F(GServicesSettingsTest, CalculateDigest) {
GServicesSettings::SettingsMap settings_map;
settings_map["android_id"] = "55XXXXXXXXXXXXXXXX0";
settings_map["checkin_interval"] = "86400";
settings_map["checkin_url"] =
"https://fake.address.google.com/canary/checkin";
settings_map["chrome_device"] = "1";
settings_map["device_country"] = "us";
settings_map["gcm_hostname"] = "fake.address.google.com";
settings_map["gcm_secure_port"] = "443";
EXPECT_EQ("1-33381ccd1cf5791dc0e6dfa234266fa9f1259197",
GServicesSettings::CalculateDigest(settings_map));
}
// Verifies that settings are not updated when load result is empty.
TEST_F(GServicesSettingsTest, UpdateFromEmptyLoadResult) {
GCMStore::LoadResult result;
result.gservices_digest = "digest_value";
result.gservices_digest = "";
settings().UpdateFromLoadResult(result);
CheckAllSetToDefault();
EXPECT_EQ(std::string(), settings().digest());
EXPECT_EQ(kDefaultSettingsDigest, settings().digest());
}
// Verifies that settings are not updated when one of them is missing.
// Verifies that settings are not when digest value does not match.
TEST_F(GServicesSettingsTest, UpdateFromLoadResultWithSettingMissing) {
GCMStore::LoadResult result;
result.gservices_settings = alternative_settings();
result.gservices_settings["checkin_internval"] = "100000";
result.gservices_digest = "digest_value";
result.gservices_settings.erase("gcm_hostname");
settings().UpdateFromLoadResult(result);
CheckAllSetToDefault();
EXPECT_EQ(std::string(), settings().digest());
EXPECT_EQ(kDefaultSettingsDigest, settings().digest());
}
// Verifies that the settings are set correctly based on the load result.
TEST_F(GServicesSettingsTest, UpdateFromLoadResult) {
GCMStore::LoadResult result;
result.gservices_settings = alternative_settings();
result.gservices_digest = "digest_value";
result.gservices_settings["checkin_interval"] =
base::Int64ToString(kAlternativeCheckinInterval);
result.gservices_settings["checkin_url"] = kAlternativeCheckinURL;
result.gservices_settings["gcm_hostname"] = kAlternativeMCSHostname;
result.gservices_settings["gcm_secure_port"] =
base::IntToString(kAlternativeMCSSecurePort);
result.gservices_settings["gcm_registration_url"] =
kAlternativeRegistrationURL;
result.gservices_digest = kAlternativeSettingsDigest;
settings().UpdateFromLoadResult(result);
CheckAllSetToAlternative();
EXPECT_EQ("digest_value", settings().digest());
EXPECT_EQ(base::TimeDelta::FromSeconds(kAlternativeCheckinInterval),
settings().GetCheckinInterval());
EXPECT_EQ(GURL(kAlternativeCheckinURL), settings().GetCheckinURL());
EXPECT_EQ(GURL("https://alternative.gcm.host:7777"),
settings().GetMCSMainEndpoint());
EXPECT_EQ(GURL("https://alternative.gcm.host:443"),
settings().GetMCSFallbackEndpoint());
EXPECT_EQ(GURL(kAlternativeRegistrationURL), settings().GetRegistrationURL());
EXPECT_EQ(GServicesSettings::CalculateDigest(result.gservices_settings),
settings().digest());
}
// Verifies that the settings are set correctly after parsing a checkin
// response.
TEST_F(GServicesSettingsTest, UpdateFromCheckinResponse) {
// Verifies that the checkin interval is updated to minimum if the original
// value is less than minimum.
TEST_F(GServicesSettingsTest, CheckinResponseMinimumCheckinInterval) {
// Setting the checkin interval to less than minimum.
checkin_proto::AndroidCheckinResponse checkin_response;
checkin_response.set_digest("digest_value");
SetWithAlternativeSettings(checkin_response);
GServicesSettings::SettingsMap new_settings;
new_settings["checkin_interval"] = "3600";
AddSettingsToResponse(checkin_response, new_settings, false);
EXPECT_TRUE(settings().UpdateFromCheckinResponse(checkin_response));
CheckAllSetToAlternative();
EXPECT_EQ(alternative_settings_, settings().GetSettingsMap());
EXPECT_EQ("digest_value", settings().digest());
EXPECT_EQ(GServicesSettings::MinimumCheckinInterval(),
settings().GetCheckinInterval());
EXPECT_EQ(GServicesSettings::CalculateDigest(new_settings),
settings().digest());
}
// Verifies that the checkin interval is updated to minimum if the original
// value is less than minimum.
TEST_F(GServicesSettingsTest, UpdateFromCheckinResponseMinimumCheckinInterval) {
// Verifies that default checkin interval can be selectively overwritten.
TEST_F(GServicesSettingsTest, CheckinResponseUpdateCheckinInterval) {
checkin_proto::AndroidCheckinResponse checkin_response;
GServicesSettings::SettingsMap new_settings;
new_settings["checkin_interval"] = "86400";
AddSettingsToResponse(checkin_response, new_settings, false);
checkin_response.set_digest("digest_value");
// Setting the checkin interval to less than minimum.
alternative_settings_["checkin_interval"] = base::IntToString(3600);
SetWithAlternativeSettings(checkin_response);
EXPECT_TRUE(settings().UpdateFromCheckinResponse(checkin_response));
// Only the checkin interval was updated:
EXPECT_EQ(base::TimeDelta::FromSeconds(86400),
settings().GetCheckinInterval());
// Other settings still set to default.
EXPECT_EQ(GURL("https://mtalk.google.com:5228"),
settings().GetMCSMainEndpoint());
EXPECT_EQ(GURL("https://mtalk.google.com:443"),
settings().GetMCSFallbackEndpoint());
EXPECT_EQ(GURL(kDefaultCheckinURL), settings().GetCheckinURL());
EXPECT_EQ(GURL(kDefaultRegistrationURL), settings().GetRegistrationURL());
EXPECT_EQ(GServicesSettings::CalculateDigest(new_settings),
settings().digest());
}
// Verifies that default registration URL can be selectively overwritten.
TEST_F(GServicesSettingsTest, CheckinResponseUpdateRegistrationURL) {
checkin_proto::AndroidCheckinResponse checkin_response;
GServicesSettings::SettingsMap new_settings;
new_settings["gcm_registration_url"] = "https://new.registration.url";
AddSettingsToResponse(checkin_response, new_settings, false);
EXPECT_TRUE(settings().UpdateFromCheckinResponse(checkin_response));
EXPECT_EQ(GServicesSettings::MinimumCheckinInterval(),
settings().checkin_interval());
EXPECT_EQ("digest_value", settings().digest());
// Only the registration URL was updated:
EXPECT_EQ(GURL("https://new.registration.url"),
settings().GetRegistrationURL());
// Other settings still set to default.
EXPECT_EQ(base::TimeDelta::FromSeconds(kDefaultCheckinInterval),
settings().GetCheckinInterval());
EXPECT_EQ(GURL("https://mtalk.google.com:5228"),
settings().GetMCSMainEndpoint());
EXPECT_EQ(GURL("https://mtalk.google.com:443"),
settings().GetMCSFallbackEndpoint());
EXPECT_EQ(GURL(kDefaultCheckinURL), settings().GetCheckinURL());
EXPECT_EQ(GServicesSettings::CalculateDigest(new_settings),
settings().digest());
}
// Verifies that settings are not updated when one of them is missing.
TEST_F(GServicesSettingsTest, UpdateFromCheckinResponseWithSettingMissing) {
// Verifies that default checkin URL can be selectively overwritten.
TEST_F(GServicesSettingsTest, CheckinResponseUpdateCheckinURL) {
checkin_proto::AndroidCheckinResponse checkin_response;
GServicesSettings::SettingsMap new_settings;
new_settings["checkin_url"] = "https://new.checkin.url";
AddSettingsToResponse(checkin_response, new_settings, false);
checkin_response.set_digest("digest_value");
alternative_settings_.erase("gcm_hostname");
SetWithAlternativeSettings(checkin_response);
EXPECT_TRUE(settings().UpdateFromCheckinResponse(checkin_response));
EXPECT_FALSE(settings().UpdateFromCheckinResponse(checkin_response));
// Only the checkin URL was updated:
EXPECT_EQ(GURL("https://new.checkin.url"), settings().GetCheckinURL());
CheckAllSetToDefault();
EXPECT_EQ(std::string(), settings().digest());
// Other settings still set to default.
EXPECT_EQ(base::TimeDelta::FromSeconds(kDefaultCheckinInterval),
settings().GetCheckinInterval());
EXPECT_EQ(GURL("https://mtalk.google.com:5228"),
settings().GetMCSMainEndpoint());
EXPECT_EQ(GURL("https://mtalk.google.com:443"),
settings().GetMCSFallbackEndpoint());
EXPECT_EQ(GURL(kDefaultRegistrationURL), settings().GetRegistrationURL());
EXPECT_EQ(GServicesSettings::CalculateDigest(new_settings),
settings().digest());
}
// Verifies that no update is done, when a checkin response misses digest.
TEST_F(GServicesSettingsTest, UpdateFromCheckinResponseNoDigest) {
// Verifies that default MCS hostname can be selectively overwritten.
TEST_F(GServicesSettingsTest, CheckinResponseUpdateMCSHostname) {
checkin_proto::AndroidCheckinResponse checkin_response;
GServicesSettings::SettingsMap new_settings;
new_settings["gcm_hostname"] = "new.gcm.hostname";
AddSettingsToResponse(checkin_response, new_settings, false);
SetWithAlternativeSettings(checkin_response);
EXPECT_FALSE(settings().UpdateFromCheckinResponse(checkin_response));
EXPECT_TRUE(settings().UpdateFromCheckinResponse(checkin_response));
CheckAllSetToDefault();
EXPECT_EQ(std::string(), settings().digest());
// Only the MCS endpoints were updated:
EXPECT_EQ(GURL("https://new.gcm.hostname:5228"),
settings().GetMCSMainEndpoint());
EXPECT_EQ(GURL("https://new.gcm.hostname:443"),
settings().GetMCSFallbackEndpoint());
// Other settings still set to default.
EXPECT_EQ(base::TimeDelta::FromSeconds(kDefaultCheckinInterval),
settings().GetCheckinInterval());
EXPECT_EQ(GURL(kDefaultCheckinURL), settings().GetCheckinURL());
EXPECT_EQ(GURL(kDefaultRegistrationURL), settings().GetRegistrationURL());
EXPECT_EQ(GServicesSettings::CalculateDigest(new_settings),
settings().digest());
}
// Verifies that no update is done, when a checkin response digest is the same.
TEST_F(GServicesSettingsTest, UpdateFromCheckinResponseSameDigest) {
GCMStore::LoadResult load_result;
load_result.gservices_digest = "old_digest";
load_result.gservices_settings = alternative_settings();
settings().UpdateFromLoadResult(load_result);
// Verifies that default MCS secure port can be selectively overwritten.
TEST_F(GServicesSettingsTest, CheckinResponseUpdateMCSSecurePort) {
checkin_proto::AndroidCheckinResponse checkin_response;
GServicesSettings::SettingsMap new_settings;
new_settings["gcm_secure_port"] = "5229";
AddSettingsToResponse(checkin_response, new_settings, false);
EXPECT_TRUE(settings().UpdateFromCheckinResponse(checkin_response));
// Only the main MCS endpoint was updated:
EXPECT_EQ(GURL("https://mtalk.google.com:5229"),
settings().GetMCSMainEndpoint());
// Other settings still set to default.
EXPECT_EQ(base::TimeDelta::FromSeconds(kDefaultCheckinInterval),
settings().GetCheckinInterval());
EXPECT_EQ(GURL(kDefaultCheckinURL), settings().GetCheckinURL());
EXPECT_EQ(GURL("https://mtalk.google.com:443"),
settings().GetMCSFallbackEndpoint());
EXPECT_EQ(GURL(kDefaultRegistrationURL), settings().GetRegistrationURL());
EXPECT_EQ(GServicesSettings::CalculateDigest(new_settings),
settings().digest());
}
// Update from checkin response should also do incremental update for both cases
// where some settings are removed or added.
TEST_F(GServicesSettingsTest, UpdateFromCheckinResponseSettingsDiff) {
checkin_proto::AndroidCheckinResponse checkin_response;
checkin_response.set_digest("old_digest");
SetWithAlternativeSettings(checkin_response);
EXPECT_FALSE(settings().UpdateFromCheckinResponse(checkin_response));
CheckAllSetToAlternative();
EXPECT_EQ("old_digest", settings().digest());
// Only the new settings will be included in the response with settings diff.
GServicesSettings::SettingsMap settings_diff;
settings_diff["new_setting_1"] = "new_setting_1_value";
settings_diff["new_setting_2"] = "new_setting_2_value";
settings_diff["gcm_secure_port"] = "5229";
// Full settings are necessary to calculate digest.
GServicesSettings::SettingsMap full_settings(settings_diff);
std::string digest = GServicesSettings::CalculateDigest(full_settings);
checkin_response.Clear();
AddSettingsToResponse(checkin_response, settings_diff, true);
EXPECT_TRUE(settings().UpdateFromCheckinResponse(checkin_response));
EXPECT_EQ(full_settings, settings().settings_map());
// Default setting overwritten by settings diff.
EXPECT_EQ(GURL("https://mtalk.google.com:5229"),
settings().GetMCSMainEndpoint());
// Setting up diff removing some of the values (including default setting).
settings_diff.clear();
settings_diff["delete_new_setting_1"] = "";
settings_diff["delete_gcm_secure_port"] = "";
settings_diff["new_setting_3"] = "new_setting_3_value";
// Updating full settings to calculate digest.
full_settings.erase(full_settings.find("new_setting_1"));
full_settings.erase(full_settings.find("gcm_secure_port"));
full_settings["new_setting_3"] = "new_setting_3_value";
digest = GServicesSettings::CalculateDigest(full_settings);
checkin_response.Clear();
AddSettingsToResponse(checkin_response, settings_diff, true);
EXPECT_TRUE(settings().UpdateFromCheckinResponse(checkin_response));
EXPECT_EQ(full_settings, settings().settings_map());
// Default setting back to norm.
EXPECT_EQ(GURL("https://mtalk.google.com:5228"),
settings().GetMCSMainEndpoint());
}
} // namespace gcm
......@@ -271,8 +271,8 @@ void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
void GCMClientImpl::InitializeMCSClient(
scoped_ptr<GCMStore::LoadResult> result) {
std::vector<GURL> endpoints;
endpoints.push_back(gservices_settings_.mcs_main_endpoint());
endpoints.push_back(gservices_settings_.mcs_fallback_endpoint());
endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
endpoints.push_back(gservices_settings_.GetMCSFallbackEndpoint());
connection_factory_ = internals_builder_->BuildConnectionFactory(
endpoints,
kDefaultBackoffPolicy,
......@@ -339,7 +339,7 @@ void GCMClientImpl::StartCheckin() {
account_ids_,
chrome_build_proto_);
checkin_request_.reset(
new CheckinRequest(gservices_settings_.checkin_url(),
new CheckinRequest(gservices_settings_.GetCheckinURL(),
request_info,
kDefaultBackoffPolicy,
base::Bind(&GCMClientImpl::OnCheckinCompleted,
......@@ -378,7 +378,7 @@ void GCMClientImpl::OnCheckinCompleted(
// First update G-services settings, as something might have changed.
if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
gcm_store_->SetGServicesSettings(
gservices_settings_.GetSettingsMap(),
gservices_settings_.settings_map(),
gservices_settings_.digest(),
base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
weak_ptr_factory_.GetWeakPtr()));
......@@ -418,7 +418,7 @@ void GCMClientImpl::SchedulePeriodicCheckin() {
}
base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
return last_checkin_time_ + gservices_settings_.checkin_interval() -
return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
clock_->Now();
}
......@@ -476,7 +476,7 @@ void GCMClientImpl::Register(const std::string& app_id,
DCHECK_EQ(0u, pending_registration_requests_.count(app_id));
RegistrationRequest* registration_request =
new RegistrationRequest(gservices_settings_.registration_url(),
new RegistrationRequest(gservices_settings_.GetRegistrationURL(),
request_info,
kDefaultBackoffPolicy,
base::Bind(&GCMClientImpl::OnRegisterCompleted,
......@@ -550,16 +550,15 @@ void GCMClientImpl::Unregister(const std::string& app_id) {
device_checkin_info_.secret,
app_id);
UnregistrationRequest* unregistration_request =
new UnregistrationRequest(
gservices_settings_.registration_url(),
request_info,
kDefaultBackoffPolicy,
base::Bind(&GCMClientImpl::OnUnregisterCompleted,
weak_ptr_factory_.GetWeakPtr(),
app_id),
url_request_context_getter_,
&recorder_);
UnregistrationRequest* unregistration_request = new UnregistrationRequest(
gservices_settings_.GetRegistrationURL(),
request_info,
kDefaultBackoffPolicy,
base::Bind(&GCMClientImpl::OnUnregisterCompleted,
weak_ptr_factory_.GetWeakPtr(),
app_id),
url_request_context_getter_,
&recorder_);
pending_unregistration_requests_[app_id] = unregistration_request;
unregistration_request->Start();
}
......
......@@ -42,7 +42,6 @@ enum LastEvent {
const uint64 kDeviceAndroidId = 54321;
const uint64 kDeviceSecurityToken = 12345;
const int64 kSettingsCheckinInterval = 16 * 60 * 60;
const char kSettingsDefaultDigest[] = "default_digest";
const char kAppId[] = "app_id";
const char kSender[] = "project_id";
const char kSender2[] = "project_id2";
......@@ -389,6 +388,7 @@ void GCMClientImplTest::CompleteCheckin(
setting->set_name(it->first);
setting->set_value(it->second);
}
response.set_settings_diff(false);
}
std::string response_string;
......@@ -687,8 +687,6 @@ class GCMClientImplCheckinTest : public GCMClientImplTest {
virtual ~GCMClientImplCheckinTest();
virtual void SetUp() OVERRIDE;
std::map<std::string, std::string> GenerateSettings(int64 checkin_interval);
};
GCMClientImplCheckinTest::GCMClientImplCheckinTest() {
......@@ -715,18 +713,20 @@ TEST_F(GCMClientImplCheckinTest, GServicesSettingsAfterInitialCheckin) {
settings["gcm_hostname"] = "alternative.gcm.host";
settings["gcm_secure_port"] = "7777";
settings["gcm_registration_url"] = "http://alternative.url/registration";
CompleteCheckin(
kDeviceAndroidId, kDeviceSecurityToken, kSettingsDefaultDigest, settings);
CompleteCheckin(kDeviceAndroidId,
kDeviceSecurityToken,
GServicesSettings::CalculateDigest(settings),
settings);
EXPECT_EQ(base::TimeDelta::FromSeconds(kSettingsCheckinInterval),
gservices_settings().checkin_interval());
gservices_settings().GetCheckinInterval());
EXPECT_EQ(GURL("http://alternative.url/checkin"),
gservices_settings().checkin_url());
gservices_settings().GetCheckinURL());
EXPECT_EQ(GURL("http://alternative.url/registration"),
gservices_settings().registration_url());
gservices_settings().GetRegistrationURL());
EXPECT_EQ(GURL("https://alternative.gcm.host:7777"),
gservices_settings().mcs_main_endpoint());
gservices_settings().GetMCSMainEndpoint());
EXPECT_EQ(GURL("https://alternative.gcm.host:443"),
gservices_settings().mcs_fallback_endpoint());
gservices_settings().GetMCSFallbackEndpoint());
}
// This test only checks that periodic checkin happens.
......@@ -737,13 +737,17 @@ TEST_F(GCMClientImplCheckinTest, PeriodicCheckin) {
settings["gcm_hostname"] = "alternative.gcm.host";
settings["gcm_secure_port"] = "7777";
settings["gcm_registration_url"] = "http://alternative.url/registration";
CompleteCheckin(
kDeviceAndroidId, kDeviceSecurityToken, kSettingsDefaultDigest, settings);
CompleteCheckin(kDeviceAndroidId,
kDeviceSecurityToken,
GServicesSettings::CalculateDigest(settings),
settings);
EXPECT_EQ(2, clock()->call_count());
PumpLoopUntilIdle();
CompleteCheckin(
kDeviceAndroidId, kDeviceSecurityToken, kSettingsDefaultDigest, settings);
CompleteCheckin(kDeviceAndroidId,
kDeviceSecurityToken,
GServicesSettings::CalculateDigest(settings),
settings);
}
TEST_F(GCMClientImplCheckinTest, LoadGSettingsFromStore) {
......@@ -753,22 +757,24 @@ TEST_F(GCMClientImplCheckinTest, LoadGSettingsFromStore) {
settings["gcm_hostname"] = "alternative.gcm.host";
settings["gcm_secure_port"] = "7777";
settings["gcm_registration_url"] = "http://alternative.url/registration";
CompleteCheckin(
kDeviceAndroidId, kDeviceSecurityToken, kSettingsDefaultDigest, settings);
CompleteCheckin(kDeviceAndroidId,
kDeviceSecurityToken,
GServicesSettings::CalculateDigest(settings),
settings);
BuildGCMClient(base::TimeDelta());
InitializeGCMClient();
EXPECT_EQ(base::TimeDelta::FromSeconds(kSettingsCheckinInterval),
gservices_settings().checkin_interval());
gservices_settings().GetCheckinInterval());
EXPECT_EQ(GURL("http://alternative.url/checkin"),
gservices_settings().checkin_url());
gservices_settings().GetCheckinURL());
EXPECT_EQ(GURL("http://alternative.url/registration"),
gservices_settings().registration_url());
gservices_settings().GetRegistrationURL());
EXPECT_EQ(GURL("https://alternative.gcm.host:7777"),
gservices_settings().mcs_main_endpoint());
gservices_settings().GetMCSMainEndpoint());
EXPECT_EQ(GURL("https://alternative.gcm.host:443"),
gservices_settings().mcs_fallback_endpoint());
gservices_settings().GetMCSFallbackEndpoint());
}
} // namespace gcm
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment