From 0e097f90d5bba038b5826ebac2870aadde28d2ca Mon Sep 17 00:00:00 2001
From: "sergeyu@chromium.org"
 <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>
Date: Tue, 14 Dec 2010 03:05:40 +0000
Subject: [PATCH] Use heartbeat interval received from the bot.

BUG=None
TEST=Unittests

Review URL: http://codereview.chromium.org/5510006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@69096 0039d316-1c4b-4281-b951-d872f2087c98
---
 remoting/host/heartbeat_sender.cc          | 57 ++++++++++++++++------
 remoting/host/heartbeat_sender.h           | 18 +++++++
 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 b4caf57fe2890..74c08c301902c 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 e2befa9bc4fb1..ef310b40b9eb9 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 c5f03a8560e5b..9b3edfcbcd03d 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
-- 
GitLab