diff options
-rw-r--r-- | remoting/host/heartbeat_sender.cc | 57 | ||||
-rw-r--r-- | remoting/host/heartbeat_sender.h | 18 | ||||
-rw-r--r-- | remoting/host/heartbeat_sender_unittest.cc | 47 |
3 files changed, 96 insertions, 26 deletions
diff --git a/remoting/host/heartbeat_sender.cc b/remoting/host/heartbeat_sender.cc index b4caf57f..74c08c3 100644 --- a/remoting/host/heartbeat_sender.cc +++ b/remoting/host/heartbeat_sender.cc @@ -16,6 +16,9 @@ #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" #include "third_party/libjingle/source/talk/xmpp/constants.h" +using buzz::QName; +using buzz::XmlElement; + namespace remoting { namespace { @@ -24,12 +27,15 @@ const char kHostIdAttr[] = "hostid"; const char kHeartbeatSignatureTag[] = "signature"; const char kSignatureTimeAttr[] = "time"; -// TODO(sergeyu): Make this configurable by the cloud. -const int64 kHeartbeatPeriodMs = 5 * 60 * 1000; // 5 minutes. +const char kHeartbeatResultTag[] = "heartbeat-result"; +const char kSetIntervalTag[] = "set-interval"; + +const int64 kDefaultHeartbeatIntervalMs = 5 * 60 * 1000; // 5 minutes. } HeartbeatSender::HeartbeatSender() - : state_(CREATED) { + : state_(CREATED), + interval_ms_(kDefaultHeartbeatIntervalMs) { } HeartbeatSender::~HeartbeatSender() { @@ -103,33 +109,56 @@ void HeartbeatSender::DoSendStanza() { // Schedule next heartbeat. jingle_client_->message_loop()->PostDelayedTask( FROM_HERE, NewRunnableMethod(this, &HeartbeatSender::DoSendStanza), - kHeartbeatPeriodMs); + interval_ms_); } } -void HeartbeatSender::ProcessResponse(const buzz::XmlElement* response) { - if (response->Attr(buzz::QN_TYPE) == buzz::STR_ERROR) { +void HeartbeatSender::ProcessResponse(const XmlElement* response) { + std::string type = response->Attr(buzz::QN_TYPE); + if (type == buzz::STR_ERROR) { LOG(ERROR) << "Received error in response to heartbeat: " << response->Str(); + return; + } + + // This method must only be called for error or result stanzas. + DCHECK_EQ(buzz::STR_RESULT, type); + + const XmlElement* result_element = + response->FirstNamed(QName(kChromotingXmlNamespace, kHeartbeatResultTag)); + if (result_element) { + const XmlElement* set_interval_element = + result_element->FirstNamed(QName(kChromotingXmlNamespace, + kSetIntervalTag)); + if (set_interval_element) { + const std::string& interval_str = set_interval_element->BodyText(); + int interval; + if (!base::StringToInt(interval_str, &interval) || interval <= 0) { + LOG(ERROR) << "Received invalid set-interval: " + << set_interval_element->Str(); + } else { + interval_ms_ = interval * base::Time::kMillisecondsPerSecond; + } + } } } -buzz::XmlElement* HeartbeatSender::CreateHeartbeatMessage() { - buzz::XmlElement* query = new buzz::XmlElement( - buzz::QName(kChromotingXmlNamespace, kHeartbeatQueryTag)); - query->AddAttr(buzz::QName(kChromotingXmlNamespace, kHostIdAttr), host_id_); +XmlElement* HeartbeatSender::CreateHeartbeatMessage() { + XmlElement* query = new XmlElement( + QName(kChromotingXmlNamespace, kHeartbeatQueryTag)); + query->AddAttr(QName(kChromotingXmlNamespace, kHostIdAttr), host_id_); query->AddElement(CreateSignature()); return query; } -buzz::XmlElement* HeartbeatSender::CreateSignature() { - buzz::XmlElement* signature_tag = new buzz::XmlElement( - buzz::QName(kChromotingXmlNamespace, kHeartbeatSignatureTag)); +XmlElement* HeartbeatSender::CreateSignature() { + XmlElement* signature_tag = new XmlElement( + QName(kChromotingXmlNamespace, kHeartbeatSignatureTag)); int64 time = static_cast<int64>(base::Time::Now().ToDoubleT()); std::string time_str(base::Int64ToString(time)); signature_tag->AddAttr( - buzz::QName(kChromotingXmlNamespace, kSignatureTimeAttr), time_str); + QName(kChromotingXmlNamespace, kSignatureTimeAttr), time_str); std::string message = jingle_client_->GetFullJid() + ' ' + time_str; std::string signature(key_pair_.GetSignature(message)); diff --git a/remoting/host/heartbeat_sender.h b/remoting/host/heartbeat_sender.h index e2befa9..ef310b4 100644 --- a/remoting/host/heartbeat_sender.h +++ b/remoting/host/heartbeat_sender.h @@ -37,6 +37,22 @@ class MutableHostConfig; // being signed is the full Jid concatenated with the time value, separated by // space. For example, for the heartbeat stanza above the message that is being // signed is "user@gmail.com/chromoting123123 1279061748". +// +// Bot sends the following result stanza in response to each heartbeat: +// +// <iq type="set" from="remoting@bot.talk.google.com" +// to="user@gmail.com/chromoting123123" id="5" xmlns="jabber:client"> +// <rem:heartbeat-result xmlns:rem="google:remoting"> +// <rem:set-interval>300</rem:set-interval> +// </rem:heartbeat> +// </iq> +// +// The set-interval tag is used to specify desired heartbeat interval +// in seconds. The heartbeat-result and the set-interval tags are +// optional. Host uses default heartbeat interval if it doesn't find +// set-interval tag in the result Iq stanza it receives from the +// server. +// // TODO(sergeyu): Is it enough to sign JID and nothing else? class HeartbeatSender : public base::RefCountedThreadSafe<HeartbeatSender> { public: @@ -61,6 +77,7 @@ class HeartbeatSender : public base::RefCountedThreadSafe<HeartbeatSender> { private: FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, DoSendStanza); FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, CreateHeartbeatMessage); + FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, ProcessResponse); enum State { CREATED, @@ -84,6 +101,7 @@ class HeartbeatSender : public base::RefCountedThreadSafe<HeartbeatSender> { scoped_ptr<IqRequest> request_; std::string host_id_; HostKeyPair key_pair_; + int interval_ms_; DISALLOW_COPY_AND_ASSIGN(HeartbeatSender); }; diff --git a/remoting/host/heartbeat_sender_unittest.cc b/remoting/host/heartbeat_sender_unittest.cc index c5f03a8..9b3edfc 100644 --- a/remoting/host/heartbeat_sender_unittest.cc +++ b/remoting/host/heartbeat_sender_unittest.cc @@ -19,6 +19,9 @@ #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" #include "third_party/libjingle/source/talk/xmpp/constants.h" +using buzz::QName; +using buzz::XmlElement; + using testing::_; using testing::DeleteArg; using testing::DoAll; @@ -46,7 +49,7 @@ class MockIqRequest : public IqRequest { } MOCK_METHOD3(SendIq, void(const std::string& type, const std::string& addressee, - buzz::XmlElement* iq_body)); + XmlElement* iq_body)); }; class HeartbeatSenderTest : public testing::Test { @@ -79,10 +82,9 @@ class HeartbeatSenderTest : public testing::Test { scoped_refptr<InMemoryHostConfig> config_; }; +// Call Start() followed by Stop(), and makes sure an Iq stanza is +// being send. TEST_F(HeartbeatSenderTest, DoSendStanza) { - // This test calls Start() followed by Stop(), and makes sure an Iq - // stanza is being send. - // |iq_request| is freed by HeartbeatSender. MockIqRequest* iq_request = new MockIqRequest(jingle_client_); @@ -102,30 +104,29 @@ TEST_F(HeartbeatSenderTest, DoSendStanza) { message_loop_.RunAllPending(); } +// Validate format of the heartbeat stanza. TEST_F(HeartbeatSenderTest, CreateHeartbeatMessage) { - // This test validates format of the heartbeat stanza. - scoped_refptr<HeartbeatSender> heartbeat_sender(new HeartbeatSender()); ASSERT_TRUE(heartbeat_sender->Init(config_, jingle_client_)); int64 start_time = static_cast<int64>(base::Time::Now().ToDoubleT()); - scoped_ptr<buzz::XmlElement> stanza( + scoped_ptr<XmlElement> stanza( heartbeat_sender->CreateHeartbeatMessage()); ASSERT_TRUE(stanza.get() != NULL); - EXPECT_TRUE(buzz::QName(kChromotingXmlNamespace, "heartbeat") == + EXPECT_TRUE(QName(kChromotingXmlNamespace, "heartbeat") == stanza->Name()); EXPECT_EQ(std::string(kHostId), - stanza->Attr(buzz::QName(kChromotingXmlNamespace, "hostid"))); + stanza->Attr(QName(kChromotingXmlNamespace, "hostid"))); - buzz::QName signature_tag(kChromotingXmlNamespace, "signature"); - buzz::XmlElement* signature = stanza->FirstNamed(signature_tag); + QName signature_tag(kChromotingXmlNamespace, "signature"); + XmlElement* signature = stanza->FirstNamed(signature_tag); ASSERT_TRUE(signature != NULL); EXPECT_TRUE(stanza->NextNamed(signature_tag) == NULL); std::string time_str = - signature->Attr(buzz::QName(kChromotingXmlNamespace, "time")); + signature->Attr(QName(kChromotingXmlNamespace, "time")); int64 time; EXPECT_TRUE(base::StringToInt64(time_str, &time)); int64 now = static_cast<int64>(base::Time::Now().ToDoubleT()); @@ -139,4 +140,26 @@ TEST_F(HeartbeatSenderTest, CreateHeartbeatMessage) { EXPECT_EQ(expected_signature, signature->BodyText()); } +// Verify that ProcessResponse parses set-interval result. +TEST_F(HeartbeatSenderTest, ProcessResponse) { + XmlElement* response = new XmlElement(QName("", "iq")); + response->AddAttr(QName("", "type"), "result"); + + XmlElement* result = new XmlElement( + QName(kChromotingXmlNamespace, "heartbeat-result")); + response->AddElement(result); + + XmlElement* set_interval = new XmlElement( + QName(kChromotingXmlNamespace, "set-interval")); + result->AddElement(set_interval); + + const int kTestInterval = 123; + set_interval->AddText(base::IntToString(kTestInterval)); + + scoped_refptr<HeartbeatSender> heartbeat_sender(new HeartbeatSender()); + heartbeat_sender->ProcessResponse(response); + + EXPECT_EQ(kTestInterval * 1000, heartbeat_sender->interval_ms_); +} + } // namespace remoting |