diff options
author | gene@chromium.org <gene@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-26 19:59:41 +0000 |
---|---|---|
committer | gene@chromium.org <gene@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-26 19:59:41 +0000 |
commit | 05861d65af900b604d9e99e4eebb61deb45de1cd (patch) | |
tree | 172427792ac96feea1189b762287b0af74b7cea9 | |
parent | fc992beca7a034a8e6437faf73377d375ac2e44a (diff) | |
download | chromium_src-05861d65af900b604d9e99e4eebb61deb45de1cd.zip chromium_src-05861d65af900b604d9e99e4eebb61deb45de1cd.tar.gz chromium_src-05861d65af900b604d9e99e4eebb61deb45de1cd.tar.bz2 |
Adding XMPP ping functionality to CloudPrint. XMPP ping and timeout is controlled thorugh Service State file.
BUG=157735
Review URL: https://chromiumcodereview.appspot.com/11232048
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@164385 0039d316-1c4b-4281-b951-d872f2087c98
27 files changed, 458 insertions, 9 deletions
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index eabe1ad..5cab24f 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -1934,6 +1934,10 @@ const char kCloudPrintRobotRefreshToken[] = "cloud_print.robot_refresh_token"; const char kCloudPrintRobotEmail[] = "cloud_print.robot_email"; // A boolean indicating whether we should connect to cloud print new printers. const char kCloudPrintConnectNewPrinters[] = "cloud_print.connect_new_printers"; +// A boolean indicating whether we should ping XMPP connection. +const char kCloudPrintXmppPingEnabled[] = "cloud_print.xmpp_ping_enabled"; +// An int value indicating the average timeout between xmpp pings. +const char kCloudPrintXmppPingTimeout[] = "cloud_print.xmpp_ping_timeout_sec"; // List of printers which should not be connected. const char kCloudPrintPrinterBlacklist[] = "cloud_print.printer_blacklist"; // A boolean indicating whether submitting jobs to Google Cloud Print is diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 0b44d49..4813b30 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -640,6 +640,8 @@ extern const char kCloudPrintEnableJobPoll[]; extern const char kCloudPrintRobotRefreshToken[]; extern const char kCloudPrintRobotEmail[]; extern const char kCloudPrintConnectNewPrinters[]; +extern const char kCloudPrintXmppPingEnabled[]; +extern const char kCloudPrintXmppPingTimeout[]; extern const char kCloudPrintPrinterBlacklist[]; extern const char kCloudPrintSubmitEnabled[]; diff --git a/chrome/service/cloud_print/cloud_print_consts.h b/chrome/service/cloud_print/cloud_print_consts.h index 2d9254e..b5fb24c 100644 --- a/chrome/service/cloud_print/cloud_print_consts.h +++ b/chrome/service/cloud_print/cloud_print_consts.h @@ -64,6 +64,15 @@ const int kCloudPrintAuthMaxRetryCount = -1; const int kMinJobPollIntervalSecs = 5*60; // 5 minutes in seconds const int kMaxJobPollIntervalSecs = 8*60; // 8 minutes in seconds +// When we have XMPP notifications available, we ping server to keep connection +// alive or check connection status. +const int kDefaultXmppPingTimeoutSecs = 5*60; // 5 minutes in seconds +const int kMinimumXmppPingTimeoutSecs = 2*60; // 2 minutes in seconds +const int kXmppPingCheckIntervalSecs = 60; + +// Number of failed pings before we try to reinstablish XMPP connection. +const int kMaxFailedXmppPings = 2; + // The number of seconds before the OAuth2 access token is due to expire that // we try and refresh it. const int kTokenRefreshGracePeriodSecs = 5*60; // 5 minutes in seconds diff --git a/chrome/service/cloud_print/cloud_print_proxy_backend.cc b/chrome/service/cloud_print/cloud_print_proxy_backend.cc index 16d7211..44df7b6 100644 --- a/chrome/service/cloud_print/cloud_print_proxy_backend.cc +++ b/chrome/service/cloud_print/cloud_print_proxy_backend.cc @@ -8,10 +8,12 @@ #include <vector> #include "base/bind.h" +#include "base/command_line.h" #include "base/compiler_specific.h" #include "base/file_util.h" #include "base/rand_util.h" #include "base/values.h" +#include "chrome/common/chrome_switches.h" #include "chrome/service/cloud_print/cloud_print_auth.h" #include "chrome/service/cloud_print/cloud_print_connector.h" #include "chrome/service/cloud_print/cloud_print_consts.h" @@ -88,6 +90,7 @@ class CloudPrintProxyBackend::Core notifier::NotificationsDisabledReason reason) OVERRIDE; virtual void OnIncomingNotification( const notifier::Notification& notification) OVERRIDE; + virtual void OnPingResponse() OVERRIDE; private: friend class base::RefCountedThreadSafe<Core>; @@ -119,6 +122,10 @@ class CloudPrintProxyBackend::Core // Schedules a task to poll for jobs. Does nothing if a task is already // scheduled. void ScheduleJobPoll(); + void PingXmppServer(); + void ScheduleXmppPing(); + void CheckXmppPingStatus(); + CloudPrintTokenStore* GetTokenStore(); // Our parent CloudPrintProxyBackend @@ -143,8 +150,13 @@ class CloudPrintProxyBackend::Core bool job_poll_scheduled_; // Indicates whether we should poll for jobs when we lose XMPP connection. bool enable_job_poll_; + // Indicates whether a task to ping xmpp server has been scheduled. + bool xmpp_ping_scheduled_; + // Number of XMPP pings pending reply from the server. + int pending_xmpp_pings_; // Connector settings. ConnectorSettings settings_; + std::string robot_email_; scoped_ptr<CloudPrintTokenStore> token_store_; DISALLOW_COPY_AND_ASSIGN(Core); @@ -240,7 +252,9 @@ CloudPrintProxyBackend::Core::Core( oauth_client_info_(oauth_client_info), notifications_enabled_(false), job_poll_scheduled_(false), - enable_job_poll_(enable_job_poll) { + enable_job_poll_(enable_job_poll), + xmpp_ping_scheduled_(false), + pending_xmpp_pings_(0) { settings_.CopyFrom(settings); } @@ -305,6 +319,7 @@ void CloudPrintProxyBackend::Core::OnAuthenticationComplete( CloudPrintTokenStore* token_store = GetTokenStore(); bool first_time = token_store->token().empty(); token_store->SetToken(access_token); + robot_email_ = robot_email; // Let the frontend know that we have authenticated. backend_->frontend_loop_->PostTask( FROM_HERE, @@ -350,6 +365,7 @@ void CloudPrintProxyBackend::Core::InitNotifications( const std::string& access_token) { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); + pending_xmpp_pings_ = 0; notifier::NotifierOptions notifier_options; notifier_options.request_context_getter = g_service_process->GetServiceURLRequestContextGetter(); @@ -430,6 +446,53 @@ void CloudPrintProxyBackend::Core::ScheduleJobPoll() { } } +void CloudPrintProxyBackend::Core::PingXmppServer() { + xmpp_ping_scheduled_ = false; + + if (!push_client_.get()) + return; + + push_client_->SendPing(); + + pending_xmpp_pings_++; + if (pending_xmpp_pings_ >= kMaxFailedXmppPings) { + // Check ping status when we close to the limit. + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&CloudPrintProxyBackend::Core::CheckXmppPingStatus, this), + base::TimeDelta::FromSeconds(kXmppPingCheckIntervalSecs)); + } + + // Schedule next ping if needed. + if (notifications_enabled_) + ScheduleXmppPing(); +} + +void CloudPrintProxyBackend::Core::ScheduleXmppPing() { + if (!settings_.xmpp_ping_enabled()) + return; + + if (!xmpp_ping_scheduled_) { + base::TimeDelta interval = base::TimeDelta::FromSeconds( + base::RandInt(settings_.xmpp_ping_timeout_sec() * 0.9, + settings_.xmpp_ping_timeout_sec() * 1.1)); + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&CloudPrintProxyBackend::Core::PingXmppServer, this), + interval); + xmpp_ping_scheduled_ = true; + } +} + +void CloudPrintProxyBackend::Core::CheckXmppPingStatus() { + if (pending_xmpp_pings_ >= kMaxFailedXmppPings) { + // Reconnect to XMPP. + pending_xmpp_pings_ = 0; + push_client_.reset(); + InitNotifications(robot_email_, GetTokenStore()->token()); + } +} + CloudPrintTokenStore* CloudPrintProxyBackend::Core::GetTokenStore() { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); if (!token_store_.get()) @@ -476,6 +539,9 @@ void CloudPrintProxyBackend::Core::OnNotificationsEnabled() { // Note that ScheduleJobPoll will not schedule again if a job poll task is // already scheduled. ScheduleJobPoll(); + + // Schedule periodic ping for XMPP notification channel. + ScheduleXmppPing(); } void CloudPrintProxyBackend::Core::OnNotificationsDisabled( @@ -494,6 +560,10 @@ void CloudPrintProxyBackend::Core::OnNotificationsDisabled( void CloudPrintProxyBackend::Core::OnIncomingNotification( const notifier::Notification& notification) { + // Since we got some notification from the server, + // reset pending ping counter to 0. + pending_xmpp_pings_ = 0; + DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); VLOG(1) << "CP_CONNECTOR: Incoming notification."; if (0 == base::strcasecmp(kCloudPrintPushNotificationsSource, @@ -501,3 +571,8 @@ void CloudPrintProxyBackend::Core::OnIncomingNotification( HandlePrinterNotification(notification.data); } +void CloudPrintProxyBackend::Core::OnPingResponse() { + pending_xmpp_pings_ = 0; + VLOG(1) << "CP_CONNECTOR: Ping response received."; +} + diff --git a/chrome/service/cloud_print/connector_settings.cc b/chrome/service/cloud_print/connector_settings.cc index 0976c42..ccfc6e1 100644 --- a/chrome/service/cloud_print/connector_settings.cc +++ b/chrome/service/cloud_print/connector_settings.cc @@ -19,7 +19,9 @@ const char kDeleteOnEnumFail[] = "delete_on_enum_fail"; ConnectorSettings::ConnectorSettings() : delete_on_enum_fail_(false), - connect_new_printers_(true) { + connect_new_printers_(true), + xmpp_ping_enabled_(false), + xmpp_ping_timeout_sec_(kDefaultXmppPingTimeoutSecs) { } ConnectorSettings::~ConnectorSettings() { @@ -56,6 +58,13 @@ void ConnectorSettings::InitFrom(ServiceProcessPrefs* prefs) { connect_new_printers_ = prefs->GetBoolean( prefs::kCloudPrintConnectNewPrinters, true); + + xmpp_ping_enabled_ = prefs->GetBoolean( + prefs::kCloudPrintXmppPingEnabled, false); + int timeout = prefs->GetInt( + prefs::kCloudPrintXmppPingTimeout, kDefaultXmppPingTimeoutSecs); + SetXmppPingTimeoutSec(timeout); + const base::ListValue* printers = prefs->GetList( prefs::kCloudPrintPrinterBlacklist); if (printers) { @@ -76,8 +85,18 @@ void ConnectorSettings::CopyFrom(const ConnectorSettings& source) { proxy_id_ = source.proxy_id(); delete_on_enum_fail_ = source.delete_on_enum_fail(); connect_new_printers_ = source.connect_new_printers(); + xmpp_ping_enabled_ = source.xmpp_ping_enabled(); + xmpp_ping_timeout_sec_ = source.xmpp_ping_timeout_sec(); printer_blacklist_ = source.printer_blacklist_; if (source.print_system_settings()) print_system_settings_.reset(source.print_system_settings()->DeepCopy()); } +void ConnectorSettings::SetXmppPingTimeoutSec(int timeout) { + xmpp_ping_timeout_sec_ = timeout; + if (xmpp_ping_timeout_sec_ < kMinimumXmppPingTimeoutSecs) { + LOG(WARNING) << + "CP_CONNECTOR: XMPP ping timeout is less then minimal value"; + xmpp_ping_timeout_sec_ = kMinimumXmppPingTimeoutSecs; + } +} diff --git a/chrome/service/cloud_print/connector_settings.h b/chrome/service/cloud_print/connector_settings.h index 9750727..4c9b086 100644 --- a/chrome/service/cloud_print/connector_settings.h +++ b/chrome/service/cloud_print/connector_settings.h @@ -42,12 +42,26 @@ class ConnectorSettings { return connect_new_printers_; }; + bool xmpp_ping_enabled() const { + return xmpp_ping_enabled_; + } + + void set_xmpp_ping_enabled(bool enabled) { + xmpp_ping_enabled_ = enabled; + } + + int xmpp_ping_timeout_sec() const { + return xmpp_ping_timeout_sec_; + } + const base::DictionaryValue* print_system_settings() const { return print_system_settings_.get(); }; bool IsPrinterBlacklisted(const std::string& name) const; + void SetXmppPingTimeoutSec(int timeout); + private: // Cloud Print server url. GURL server_url_; @@ -63,6 +77,12 @@ class ConnectorSettings { // If true register all new printers in cloud print. bool connect_new_printers_; + // Indicate if XMPP pings are enabled. + bool xmpp_ping_enabled_; + + // Indicate timeout between XMPP pings. + int xmpp_ping_timeout_sec_; + // List of printers which should not be connected. std::set<std::string> printer_blacklist_; diff --git a/chrome/service/cloud_print/connector_settings_unittest.cc b/chrome/service/cloud_print/connector_settings_unittest.cc index 6aaa6c4..09ceeef 100644 --- a/chrome/service/cloud_print/connector_settings_unittest.cc +++ b/chrome/service/cloud_print/connector_settings_unittest.cc @@ -11,6 +11,7 @@ #include "base/message_loop_proxy.h" #include "base/scoped_temp_dir.h" #include "base/values.h" +#include "chrome/service/cloud_print/cloud_print_consts.h" #include "chrome/service/service_process_prefs.h" #include "testing/gmock/include/gmock/gmock.h" @@ -28,6 +29,8 @@ const char kServiceStateContent[] = " 'service_url': 'http://cp.google.com'," " 'xmpp_auth_token': 'xmp token'," " 'connect_new_printers': false," + " 'xmpp_ping_enabled': true," + " 'xmpp_ping_timeout_sec': 256," " 'printer_blacklist': [" " 'prn1'," " 'prn2'" @@ -83,6 +86,7 @@ TEST_F(ConnectorSettingsTest, InitFromEmpty) { EXPECT_FALSE(settings.delete_on_enum_fail()); EXPECT_EQ(NULL, settings.print_system_settings()); EXPECT_TRUE(settings.connect_new_printers()); + EXPECT_FALSE(settings.xmpp_ping_enabled()); EXPECT_FALSE(settings.IsPrinterBlacklisted("prn1")); } } @@ -97,6 +101,8 @@ TEST_F(ConnectorSettingsTest, InitFromFile) { EXPECT_TRUE(settings.delete_on_enum_fail()); EXPECT_TRUE(settings.print_system_settings()); EXPECT_FALSE(settings.connect_new_printers()); + EXPECT_TRUE(settings.xmpp_ping_enabled()); + EXPECT_EQ(settings.xmpp_ping_timeout_sec(), 256); EXPECT_FALSE(settings.IsPrinterBlacklisted("prn0")); EXPECT_TRUE(settings.IsPrinterBlacklisted("prn1")); } @@ -115,6 +121,25 @@ TEST_F(ConnectorSettingsTest, CopyFrom) { EXPECT_EQ(settings1.print_system_settings()->size(), settings2.print_system_settings()->size()); EXPECT_EQ(settings1.connect_new_printers(), settings2.connect_new_printers()); + EXPECT_EQ(settings1.xmpp_ping_enabled(), settings2.xmpp_ping_enabled()); + EXPECT_EQ(settings1.xmpp_ping_timeout_sec(), + settings2.xmpp_ping_timeout_sec()); EXPECT_TRUE(settings2.IsPrinterBlacklisted("prn1")); } +TEST_F(ConnectorSettingsTest, SettersTest) { + scoped_ptr<ServiceProcessPrefs> prefs(CreateTestFile("{}")); + ConnectorSettings settings; + settings.InitFrom(prefs.get()); + EXPECT_FALSE(settings.xmpp_ping_enabled()); + + // Set and check valid settings. + settings.set_xmpp_ping_enabled(true); + settings.SetXmppPingTimeoutSec(256); + EXPECT_TRUE(settings.xmpp_ping_enabled()); + EXPECT_EQ(settings.xmpp_ping_timeout_sec(), 256); + + // Set invalid settings, and check correct defaults. + settings.SetXmppPingTimeoutSec(1); + EXPECT_EQ(settings.xmpp_ping_timeout_sec(), kMinimumXmppPingTimeoutSecs); +} diff --git a/chrome/service/service_process_prefs.cc b/chrome/service/service_process_prefs.cc index 7de70a4..811647d 100644 --- a/chrome/service/service_process_prefs.cc +++ b/chrome/service/service_process_prefs.cc @@ -54,6 +54,21 @@ void ServiceProcessPrefs::SetBoolean(const std::string& key, bool value) { prefs_->SetValue(key, Value::CreateBooleanValue(value)); } +int ServiceProcessPrefs::GetInt(const std::string& key, + int default_value) const { + const Value* value; + int result = default_value; + if (prefs_->GetValue(key, &value) != PersistentPrefStore::READ_OK || + !value->GetAsInteger(&result)) { + return default_value; + } + return result; +} + +void ServiceProcessPrefs::SetInt(const std::string& key, int value) { + prefs_->SetValue(key, Value::CreateIntegerValue(value)); +} + const DictionaryValue* ServiceProcessPrefs::GetDictionary( const std::string& key) const { const Value* value; diff --git a/chrome/service/service_process_prefs.h b/chrome/service/service_process_prefs.h index 2bc3e5fe..018ce1d 100644 --- a/chrome/service/service_process_prefs.h +++ b/chrome/service/service_process_prefs.h @@ -43,6 +43,12 @@ class ServiceProcessPrefs { // Set a boolean |value| for |key|. void SetBoolean(const std::string& key, bool value); + // Returns an int preference for |key|. + int GetInt(const std::string& key, int default_value) const; + + // Set an int |value| for |key|. + void SetInt(const std::string& key, int value); + // Returns a dictionary preference for |key|. const base::DictionaryValue* GetDictionary(const std::string& key) const; diff --git a/jingle/jingle.gyp b/jingle/jingle.gyp index b1270b6..95a99ae 100644 --- a/jingle/jingle.gyp +++ b/jingle/jingle.gyp @@ -95,6 +95,8 @@ 'notifier/listener/push_notifications_send_update_task.h', 'notifier/listener/push_notifications_subscribe_task.cc', 'notifier/listener/push_notifications_subscribe_task.h', + 'notifier/listener/send_ping_task.cc', + 'notifier/listener/send_ping_task.h', 'notifier/listener/xml_element_util.cc', 'notifier/listener/xml_element_util.h', 'notifier/listener/xmpp_push_client.cc', @@ -179,6 +181,7 @@ 'notifier/listener/push_client_unittest.cc', 'notifier/listener/push_notifications_send_update_task_unittest.cc', 'notifier/listener/push_notifications_subscribe_task_unittest.cc', + 'notifier/listener/send_ping_task_unittest.cc', 'notifier/listener/xml_element_util_unittest.cc', 'notifier/listener/xmpp_push_client_unittest.cc', 'run_all_unittests.cc', diff --git a/jingle/notifier/listener/fake_push_client.cc b/jingle/notifier/listener/fake_push_client.cc index 0565c3c..1106c93 100644 --- a/jingle/notifier/listener/fake_push_client.cc +++ b/jingle/notifier/listener/fake_push_client.cc @@ -8,7 +8,7 @@ namespace notifier { -FakePushClient::FakePushClient() {} +FakePushClient::FakePushClient() : sent_pings_(0) {} FakePushClient::~FakePushClient() {} @@ -35,6 +35,10 @@ void FakePushClient::SendNotification(const Notification& notification) { sent_notifications_.push_back(notification); } +void FakePushClient::SendPing() { + sent_pings_++; +} + void FakePushClient::EnableNotifications() { FOR_EACH_OBSERVER(PushClientObserver, observers_, OnNotificationsEnabled()); @@ -68,5 +72,9 @@ const std::vector<Notification>& FakePushClient::sent_notifications() const { return sent_notifications_; } +int FakePushClient::sent_pings() const { + return sent_pings_; +} + } // namespace notifier diff --git a/jingle/notifier/listener/fake_push_client.h b/jingle/notifier/listener/fake_push_client.h index 90e086c..2c1340d 100644 --- a/jingle/notifier/listener/fake_push_client.h +++ b/jingle/notifier/listener/fake_push_client.h @@ -29,6 +29,7 @@ class FakePushClient : public PushClient { virtual void UpdateCredentials( const std::string& email, const std::string& token) OVERRIDE; virtual void SendNotification(const Notification& notification) OVERRIDE; + virtual void SendPing() OVERRIDE; // Triggers OnNotificationsEnabled on all observers. void EnableNotifications(); @@ -43,6 +44,7 @@ class FakePushClient : public PushClient { const std::string& email() const; const std::string& token() const; const std::vector<Notification>& sent_notifications() const; + int sent_pings() const; private: ObserverList<PushClientObserver> observers_; @@ -50,6 +52,7 @@ class FakePushClient : public PushClient { std::string email_; std::string token_; std::vector<Notification> sent_notifications_; + int sent_pings_; DISALLOW_COPY_AND_ASSIGN(FakePushClient); }; diff --git a/jingle/notifier/listener/non_blocking_push_client.cc b/jingle/notifier/listener/non_blocking_push_client.cc index 5765d37..c42561e 100644 --- a/jingle/notifier/listener/non_blocking_push_client.cc +++ b/jingle/notifier/listener/non_blocking_push_client.cc @@ -38,12 +38,14 @@ class NonBlockingPushClient::Core void UpdateSubscriptions(const SubscriptionList& subscriptions); void UpdateCredentials(const std::string& email, const std::string& token); void SendNotification(const Notification& data); + void SendPing(); virtual void OnNotificationsEnabled() OVERRIDE; virtual void OnNotificationsDisabled( NotificationsDisabledReason reason) OVERRIDE; virtual void OnIncomingNotification( const Notification& notification) OVERRIDE; + virtual void OnPingResponse() OVERRIDE; private: friend class base::RefCountedThreadSafe<NonBlockingPushClient::Core>; @@ -110,6 +112,12 @@ void NonBlockingPushClient::Core::SendNotification( delegate_push_client_->SendNotification(notification); } +void NonBlockingPushClient::Core::SendPing() { + DCHECK(delegate_task_runner_->BelongsToCurrentThread()); + DCHECK(delegate_push_client_.get()); + delegate_push_client_->SendPing(); +} + void NonBlockingPushClient::Core::OnNotificationsEnabled() { DCHECK(delegate_task_runner_->BelongsToCurrentThread()); parent_task_runner_->PostTask( @@ -136,6 +144,14 @@ void NonBlockingPushClient::Core::OnIncomingNotification( parent_push_client_, notification)); } +void NonBlockingPushClient::Core::OnPingResponse() { + DCHECK(delegate_task_runner_->BelongsToCurrentThread()); + parent_task_runner_->PostTask( + FROM_HERE, + base::Bind(&NonBlockingPushClient::OnPingResponse, + parent_push_client_)); +} + NonBlockingPushClient::NonBlockingPushClient( const scoped_refptr<base::SingleThreadTaskRunner>& delegate_task_runner, const CreateBlockingPushClientCallback& @@ -195,6 +211,13 @@ void NonBlockingPushClient::SendNotification( notification)); } +void NonBlockingPushClient::SendPing() { + DCHECK(thread_checker_.CalledOnValidThread()); + delegate_task_runner_->PostTask( + FROM_HERE, + base::Bind(&NonBlockingPushClient::Core::SendPing, core_.get())); +} + void NonBlockingPushClient::OnNotificationsEnabled() { DCHECK(thread_checker_.CalledOnValidThread()); FOR_EACH_OBSERVER(PushClientObserver, observers_, @@ -215,4 +238,9 @@ void NonBlockingPushClient::OnIncomingNotification( OnIncomingNotification(notification)); } +void NonBlockingPushClient::OnPingResponse() { + DCHECK(thread_checker_.CalledOnValidThread()); + FOR_EACH_OBSERVER(PushClientObserver, observers_, OnPingResponse()); +} + } // namespace notifier diff --git a/jingle/notifier/listener/non_blocking_push_client.h b/jingle/notifier/listener/non_blocking_push_client.h index 434f5f4..f5c6bc4 100644 --- a/jingle/notifier/listener/non_blocking_push_client.h +++ b/jingle/notifier/listener/non_blocking_push_client.h @@ -49,6 +49,7 @@ class NonBlockingPushClient : public PushClient { virtual void UpdateCredentials( const std::string& email, const std::string& token) OVERRIDE; virtual void SendNotification(const Notification& notification) OVERRIDE; + virtual void SendPing() OVERRIDE; private: class Core; @@ -56,6 +57,7 @@ class NonBlockingPushClient : public PushClient { void OnNotificationsEnabled(); void OnNotificationsDisabled(NotificationsDisabledReason reason); void OnIncomingNotification(const Notification& notification); + void OnPingResponse(); base::ThreadChecker thread_checker_; base::WeakPtrFactory<NonBlockingPushClient> weak_ptr_factory_; diff --git a/jingle/notifier/listener/non_blocking_push_client_unittest.cc b/jingle/notifier/listener/non_blocking_push_client_unittest.cc index f9e0282..8908910 100644 --- a/jingle/notifier/listener/non_blocking_push_client_unittest.cc +++ b/jingle/notifier/listener/non_blocking_push_client_unittest.cc @@ -112,6 +112,14 @@ TEST_F(NonBlockingPushClientTest, SendNotification) { fake_push_client_->sent_notifications()[0].Equals(notification)); } +// Make sure SendPing() gets delegated properly. +TEST_F(NonBlockingPushClientTest, SendPing) { + push_client_->SendPing(); + EXPECT_EQ(0, fake_push_client_->sent_pings()); + message_loop_.RunAllPending(); + ASSERT_EQ(1, fake_push_client_->sent_pings()); +} + // Make sure notification state changes get propagated back to the // parent. TEST_F(NonBlockingPushClientTest, NotificationStateChange) { diff --git a/jingle/notifier/listener/push_client.h b/jingle/notifier/listener/push_client.h index f80f422..fd67337 100644 --- a/jingle/notifier/listener/push_client.h +++ b/jingle/notifier/listener/push_client.h @@ -50,6 +50,9 @@ class PushClient { // Sends a notification (with no reliability guarantees). virtual void SendNotification(const Notification& notification) = 0; + + // Sends a ping (with no reliability guarantees). + virtual void SendPing() = 0; }; } // namespace notifier diff --git a/jingle/notifier/listener/push_client_observer.cc b/jingle/notifier/listener/push_client_observer.cc index 04a877b..a958eea 100644 --- a/jingle/notifier/listener/push_client_observer.cc +++ b/jingle/notifier/listener/push_client_observer.cc @@ -8,4 +8,6 @@ namespace notifier { PushClientObserver::~PushClientObserver() {} +void PushClientObserver::OnPingResponse() {} + } // namespace notifier diff --git a/jingle/notifier/listener/push_client_observer.h b/jingle/notifier/listener/push_client_observer.h index d51deb2..d153ff0 100644 --- a/jingle/notifier/listener/push_client_observer.h +++ b/jingle/notifier/listener/push_client_observer.h @@ -39,6 +39,10 @@ class PushClientObserver { // Called when a notification is received. The details of the // notification are in |notification|. virtual void OnIncomingNotification(const Notification& notification) = 0; + + // Called when a ping response is received. Default implementation does + // nothing. + virtual void OnPingResponse(); }; } // namespace notifier diff --git a/jingle/notifier/listener/push_notifications_listen_task.cc b/jingle/notifier/listener/push_notifications_listen_task.cc index ad1da9f..2e6b2ca 100644 --- a/jingle/notifier/listener/push_notifications_listen_task.cc +++ b/jingle/notifier/listener/push_notifications_listen_task.cc @@ -18,6 +18,9 @@ namespace notifier { +PushNotificationsListenTask::Delegate::~Delegate() { +} + PushNotificationsListenTask::PushNotificationsListenTask( buzz::XmppTaskParentInterface* parent, Delegate* delegate) : buzz::XmppTask(parent, buzz::XmppEngine::HL_TYPE), diff --git a/jingle/notifier/listener/push_notifications_listen_task.h b/jingle/notifier/listener/push_notifications_listen_task.h index daf48d9..62d408b 100644 --- a/jingle/notifier/listener/push_notifications_listen_task.h +++ b/jingle/notifier/listener/push_notifications_listen_task.h @@ -31,7 +31,7 @@ class PushNotificationsListenTask : public buzz::XmppTask { virtual void OnNotificationReceived(const Notification& notification) = 0; protected: - virtual ~Delegate() {} + virtual ~Delegate(); }; PushNotificationsListenTask(buzz::XmppTaskParentInterface* parent, diff --git a/jingle/notifier/listener/push_notifications_send_update_task.cc b/jingle/notifier/listener/push_notifications_send_update_task.cc index 50ec15e..4e0c127 100644 --- a/jingle/notifier/listener/push_notifications_send_update_task.cc +++ b/jingle/notifier/listener/push_notifications_send_update_task.cc @@ -32,7 +32,7 @@ int PushNotificationsSendUpdateTask::ProcessStart() { DVLOG(1) << "Sending notification " << notification_.ToString() << " as stanza " << XmlElementToString(*stanza); if (SendStanza(stanza.get()) != buzz::XMPP_RETURN_OK) { - LOG(WARNING) << "Could not send stanza " << XmlElementToString(*stanza); + DLOG(WARNING) << "Could not send stanza " << XmlElementToString(*stanza); } return STATE_DONE; } @@ -72,8 +72,8 @@ buzz::XmlElement* PushNotificationsSendUpdateTask::MakeUpdateMessage( if (!recipient.user_specific_data.empty()) { std::string base64_data; if (!base::Base64Encode(recipient.user_specific_data, &base64_data)) { - LOG(WARNING) << "Could not encode data " - << recipient.user_specific_data; + DLOG(WARNING) << "Could not encode data " + << recipient.user_specific_data; } else { recipient_element->SetBodyText(base64_data); } @@ -83,7 +83,7 @@ buzz::XmlElement* PushNotificationsSendUpdateTask::MakeUpdateMessage( buzz::XmlElement* data = new buzz::XmlElement(kQnData, true); std::string base64_data; if (!base::Base64Encode(notification.data, &base64_data)) { - LOG(WARNING) << "Could not encode data " << notification.data; + DLOG(WARNING) << "Could not encode data " << notification.data; } data->SetBodyText(base64_data); push->AddElement(data); diff --git a/jingle/notifier/listener/send_ping_task.cc b/jingle/notifier/listener/send_ping_task.cc new file mode 100644 index 0000000..90b7196 --- /dev/null +++ b/jingle/notifier/listener/send_ping_task.cc @@ -0,0 +1,80 @@ +// Copyright (c) 2012 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 "jingle/notifier/listener/send_ping_task.h" + +#include <string> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "jingle/notifier/listener/xml_element_util.h" +#include "talk/xmllite/qname.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmpp/constants.h" +#include "talk/xmpp/jid.h" +#include "talk/xmpp/xmppclient.h" + +namespace notifier { + +SendPingTask::Delegate::~Delegate() { +} + +SendPingTask::SendPingTask(buzz::XmppTaskParentInterface* parent, + Delegate* delegate) + : XmppTask(parent, buzz::XmppEngine::HL_SINGLE), delegate_(delegate) { +} + +SendPingTask::~SendPingTask() { +} + +int SendPingTask::ProcessStart() { + ping_task_id_ = task_id(); + scoped_ptr<buzz::XmlElement> stanza(MakePingStanza(ping_task_id_)); + DVLOG(1) << "Sending ping stanza " << XmlElementToString(*stanza); + if (SendStanza(stanza.get()) != buzz::XMPP_RETURN_OK) { + DLOG(WARNING) << "Could not send stanza " << XmlElementToString(*stanza); + return STATE_ERROR; + } + return STATE_RESPONSE; +} + +int SendPingTask::ProcessResponse() { + const buzz::XmlElement* stanza = NextStanza(); + if (stanza == NULL) { + return STATE_BLOCKED; + } + + DVLOG(1) << "Received stanza " << XmlElementToString(*stanza); + + std::string type = stanza->Attr(buzz::QN_TYPE); + if (type != buzz::STR_RESULT) { + DLOG(WARNING) << "No type=\"result\" attribute found in stanza " + << XmlElementToString(*stanza); + return STATE_ERROR; + } + + delegate_->OnPingResponseReceived(); + return STATE_DONE; +} + +bool SendPingTask::HandleStanza(const buzz::XmlElement* stanza) { + // MatchResponseIq() matches the given Jid with the "from" field of the given + // stanza, which in this case should be the empty string + // (signifying the server). + if (MatchResponseIq(stanza, buzz::Jid(buzz::STR_EMPTY), ping_task_id_)) { + QueueStanza(stanza); + return true; + } + return false; +} + +buzz::XmlElement* SendPingTask::MakePingStanza(const std::string& task_id) { + buzz::XmlElement* stanza = MakeIq(buzz::STR_GET, + buzz::Jid(buzz::STR_EMPTY), + task_id); + stanza->AddElement(new buzz::XmlElement(buzz::QN_PING)); + return stanza; +} + +} // namespace notifier diff --git a/jingle/notifier/listener/send_ping_task.h b/jingle/notifier/listener/send_ping_task.h new file mode 100644 index 0000000..9cd8b22 --- /dev/null +++ b/jingle/notifier/listener/send_ping_task.h @@ -0,0 +1,54 @@ +// Copyright (c) 2012 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. +// +// Methods for sending the update stanza to notify peers via xmpp. + +#ifndef JINGLE_NOTIFIER_LISTENER_SEND_PING_TASK_H_ +#define JINGLE_NOTIFIER_LISTENER_SEND_PING_TASK_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "talk/xmpp/xmpptask.h" + +namespace buzz { +class XmlElement; +} // namespace + +namespace notifier { + +class SendPingTask : public buzz::XmppTask { + public: + class Delegate { + public: + virtual void OnPingResponseReceived() = 0; + + protected: + virtual ~Delegate(); + }; + + SendPingTask(buzz::XmppTaskParentInterface* parent, Delegate* delegate); + virtual ~SendPingTask(); + + // Overridden from buzz::XmppTask. + virtual int ProcessStart() OVERRIDE; + virtual int ProcessResponse() OVERRIDE; + virtual bool HandleStanza(const buzz::XmlElement* stanza) OVERRIDE; + + private: + static buzz::XmlElement* MakePingStanza(const std::string& task_id); + + FRIEND_TEST_ALL_PREFIXES(SendPingTaskTest, MakePingStanza); + + std::string ping_task_id_; + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(SendPingTask); +}; + +typedef SendPingTask::Delegate SendPingTaskDelegate; + +} // namespace notifier + +#endif // JINGLE_NOTIFIER_LISTENER_SEND_PING_TASK_H_ diff --git a/jingle/notifier/listener/send_ping_task_unittest.cc b/jingle/notifier/listener/send_ping_task_unittest.cc new file mode 100644 index 0000000..1a2dd4b --- /dev/null +++ b/jingle/notifier/listener/send_ping_task_unittest.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2012 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 "jingle/notifier/listener/send_ping_task.h" + +#include "base/base64.h" +#include "base/memory/scoped_ptr.h" +#include "jingle/notifier/listener/xml_element_util.h" +#include "talk/xmpp/jid.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace buzz { +class XmlElement; +} + +namespace notifier { + +class SendPingTaskTest : public testing::Test { + public: + SendPingTaskTest() {} + + private: + DISALLOW_COPY_AND_ASSIGN(SendPingTaskTest); +}; + +TEST_F(SendPingTaskTest, MakePingStanza) { + std::string task_id = "42"; + + scoped_ptr<buzz::XmlElement> message(SendPingTask::MakePingStanza(task_id)); + + std::string expected_xml_string("<cli:iq type=\"get\" id=\""); + expected_xml_string += task_id; + expected_xml_string += + "\" xmlns:cli=\"jabber:client\">" + "<ping:ping xmlns:ping=\"urn:xmpp:ping\"/></cli:iq>"; + + EXPECT_EQ(expected_xml_string, XmlElementToString(*message)); +} + +} // namespace notifier diff --git a/jingle/notifier/listener/xmpp_push_client.cc b/jingle/notifier/listener/xmpp_push_client.cc index 9b4811e..76ee4ee 100644 --- a/jingle/notifier/listener/xmpp_push_client.cc +++ b/jingle/notifier/listener/xmpp_push_client.cc @@ -8,6 +8,7 @@ #include "base/message_loop_proxy.h" #include "jingle/notifier/base/notifier_options_util.h" #include "jingle/notifier/listener/push_client_observer.h" +#include "jingle/notifier/listener/send_ping_task.h" #include "jingle/notifier/listener/push_notifications_send_update_task.h" namespace notifier { @@ -80,6 +81,11 @@ void XmppPushClient::OnNotificationReceived( OnIncomingNotification(notification)); } +void XmppPushClient::OnPingResponseReceived() { + DCHECK(thread_checker_.CalledOnValidThread()); + FOR_EACH_OBSERVER(PushClientObserver, observers_, OnPingResponse()); +} + void XmppPushClient::OnSubscribed() { DCHECK(thread_checker_.CalledOnValidThread()); FOR_EACH_OBSERVER(PushClientObserver, observers_, @@ -143,4 +149,15 @@ void XmppPushClient::SendNotification(const Notification& notification) { task->Start(); } +void XmppPushClient::SendPing() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!base_task_.get()) { + DVLOG(1) << "Push: Cannot send ping"; + return; + } + // Owned by |base_task_|. + SendPingTask* task = new SendPingTask(base_task_, this); + task->Start(); +} + } // namespace notifier diff --git a/jingle/notifier/listener/xmpp_push_client.h b/jingle/notifier/listener/xmpp_push_client.h index 64725d8..08a1811 100644 --- a/jingle/notifier/listener/xmpp_push_client.h +++ b/jingle/notifier/listener/xmpp_push_client.h @@ -21,6 +21,7 @@ #include "jingle/notifier/listener/push_client.h" #include "jingle/notifier/listener/push_notifications_listen_task.h" #include "jingle/notifier/listener/push_notifications_subscribe_task.h" +#include "jingle/notifier/listener/send_ping_task.h" #include "talk/xmpp/xmppclientsettings.h" namespace buzz { @@ -36,7 +37,8 @@ class XmppPushClient : public PushClient, public Login::Delegate, public PushNotificationsListenTaskDelegate, - public PushNotificationsSubscribeTaskDelegate { + public PushNotificationsSubscribeTaskDelegate, + public SendPingTaskDelegate { public: explicit XmppPushClient(const NotifierOptions& notifier_options); virtual ~XmppPushClient(); @@ -49,6 +51,7 @@ class XmppPushClient : virtual void UpdateCredentials( const std::string& email, const std::string& token) OVERRIDE; virtual void SendNotification(const Notification& notification) OVERRIDE; + virtual void SendPing() OVERRIDE; // Login::Delegate implementation. virtual void OnConnect( @@ -64,6 +67,9 @@ class XmppPushClient : virtual void OnSubscribed() OVERRIDE; virtual void OnSubscriptionError() OVERRIDE; + // SendPingTaskDelegate implementation. + virtual void OnPingResponseReceived() OVERRIDE; + private: base::ThreadChecker thread_checker_; const NotifierOptions notifier_options_; diff --git a/jingle/notifier/listener/xmpp_push_client_unittest.cc b/jingle/notifier/listener/xmpp_push_client_unittest.cc index 3487479..3beb488 100644 --- a/jingle/notifier/listener/xmpp_push_client_unittest.cc +++ b/jingle/notifier/listener/xmpp_push_client_unittest.cc @@ -27,6 +27,7 @@ class MockObserver : public PushClientObserver { MOCK_METHOD0(OnNotificationsEnabled, void()); MOCK_METHOD1(OnNotificationsDisabled, void(NotificationsDisabledReason)); MOCK_METHOD1(OnIncomingNotification, void(const Notification&)); + MOCK_METHOD0(OnPingResponse, void()); }; class XmppPushClientTest : public testing::Test { @@ -110,6 +111,17 @@ TEST_F(XmppPushClientTest, SendNotification) { xmpp_push_client_->SendNotification(Notification()); } +// Make sure nothing blows up when the XMPP push client sends a ping. +// +// TODO(akalin): Figure out how to test that the ping was actually sent. +TEST_F(XmppPushClientTest, SendPing) { + EXPECT_CALL(mock_observer_, OnNotificationsEnabled()); + + xmpp_push_client_->OnConnect(fake_base_task_.AsWeakPtr()); + xmpp_push_client_->OnSubscribed(); + xmpp_push_client_->SendPing(); +} + // Make sure nothing blows up when the XMPP push client sends a // notification when disconnected, and the client connects. // |