1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef REMOTING_HOST_HEARTBEAT_SENDER_H_ 6#define REMOTING_HOST_HEARTBEAT_SENDER_H_ 7 8#include <string> 9 10#include "base/compiler_specific.h" 11#include "base/gtest_prod_util.h" 12#include "base/memory/ref_counted.h" 13#include "base/memory/scoped_ptr.h" 14#include "base/timer/timer.h" 15#include "remoting/base/rsa_key_pair.h" 16#include "remoting/jingle_glue/signal_strategy.h" 17 18namespace base { 19class MessageLoopProxy; 20} // namespace base 21 22namespace buzz { 23class XmlElement; 24} // namespace buzz 25 26namespace remoting { 27 28class RsaKeyPair; 29class IqRequest; 30class IqSender; 31 32// HeartbeatSender periodically sends heartbeat stanzas to the Chromoting Bot. 33// Each heartbeat stanza looks as follows: 34// 35// <iq type="set" to="remoting@bot.talk.google.com" 36// from="user@gmail.com/chromoting123123" id="5" xmlns="jabber:client"> 37// <rem:heartbeat rem:hostid="a1ddb11e-8aef-11df-bccf-18a905b9cb5a" 38// rem:sequence-id="456" 39// xmlns:rem="google:remoting"> 40// <rem:signature>.signature.</rem:signature> 41// </rem:heartbeat> 42// </iq> 43// 44// The sequence-id attribute of the heartbeat is a zero-based incrementally 45// increasing integer unique to each heartbeat from a single host. 46// The Bot checks the value, and if it is incorrect, includes the 47// correct value in the result stanza. The host should then send another 48// heartbeat, with the correct sequence-id, and increment the sequence-id in 49// susbequent heartbeats. 50// The signature is a base-64 encoded SHA-1 hash, signed with the host's 51// private RSA key. The message being signed is the full Jid concatenated with 52// the sequence-id, separated by one space. For example, for the heartbeat 53// stanza above, the message that is signed is 54// "user@gmail.com/chromoting123123 456". 55// 56// The Bot sends the following result stanza in response to each successful 57// heartbeat: 58// 59// <iq type="set" from="remoting@bot.talk.google.com" 60// to="user@gmail.com/chromoting123123" id="5" xmlns="jabber:client"> 61// <rem:heartbeat-result xmlns:rem="google:remoting"> 62// <rem:set-interval>300</rem:set-interval> 63// </rem:heartbeat-result> 64// </iq> 65// 66// The set-interval tag is used to specify desired heartbeat interval 67// in seconds. The heartbeat-result and the set-interval tags are 68// optional. Host uses default heartbeat interval if it doesn't find 69// set-interval tag in the result Iq stanza it receives from the 70// server. 71// If the heartbeat's sequence-id was incorrect, the Bot sends a result 72// stanza of this form: 73// 74// <iq type="set" from="remoting@bot.talk.google.com" 75// to="user@gmail.com/chromoting123123" id="5" xmlns="jabber:client"> 76// <rem:heartbeat-result xmlns:rem="google:remoting"> 77// <rem:expected-sequence-id>654</rem:expected-sequence-id> 78// </rem:heartbeat-result> 79// </iq> 80class HeartbeatSender : public SignalStrategy::Listener { 81 public: 82 class Listener { 83 public: 84 virtual ~Listener() { } 85 86 // Invoked after the first successful heartbeat. 87 virtual void OnHeartbeatSuccessful() = 0; 88 89 // Invoked when the host ID is permanently not recognized by the server. 90 virtual void OnUnknownHostIdError() = 0; 91 }; 92 93 // |signal_strategy| and |delegate| must outlive this 94 // object. Heartbeats will start when the supplied SignalStrategy 95 // enters the CONNECTED state. 96 HeartbeatSender(Listener* listener, 97 const std::string& host_id, 98 SignalStrategy* signal_strategy, 99 scoped_refptr<RsaKeyPair> key_pair, 100 const std::string& directory_bot_jid); 101 virtual ~HeartbeatSender(); 102 103 // SignalStrategy::Listener interface. 104 virtual void OnSignalStrategyStateChange( 105 SignalStrategy::State state) OVERRIDE; 106 virtual bool OnSignalStrategyIncomingStanza( 107 const buzz::XmlElement* stanza) OVERRIDE; 108 109 private: 110 FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, DoSendStanza); 111 FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, 112 DoSendStanzaWithExpectedSequenceId); 113 FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, CreateHeartbeatMessage); 114 FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, ProcessResponseSetInterval); 115 FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, 116 ProcessResponseExpectedSequenceId); 117 118 void SendStanza(); 119 void ResendStanza(); 120 void DoSendStanza(); 121 void ProcessResponse(IqRequest* request, const buzz::XmlElement* response); 122 void SetInterval(int interval); 123 void SetSequenceId(int sequence_id); 124 125 // Helper methods used by DoSendStanza() to generate heartbeat stanzas. 126 scoped_ptr<buzz::XmlElement> CreateHeartbeatMessage(); 127 scoped_ptr<buzz::XmlElement> CreateSignature(); 128 129 Listener* listener_; 130 std::string host_id_; 131 SignalStrategy* signal_strategy_; 132 scoped_refptr<RsaKeyPair> key_pair_; 133 std::string directory_bot_jid_; 134 scoped_ptr<IqSender> iq_sender_; 135 scoped_ptr<IqRequest> request_; 136 int interval_ms_; 137 base::RepeatingTimer<HeartbeatSender> timer_; 138 base::OneShotTimer<HeartbeatSender> timer_resend_; 139 int sequence_id_; 140 bool sequence_id_was_set_; 141 int sequence_id_recent_set_num_; 142 bool heartbeat_succeeded_; 143 int failed_startup_heartbeat_count_; 144 145 DISALLOW_COPY_AND_ASSIGN(HeartbeatSender); 146}; 147 148} // namespace remoting 149 150#endif // REMOTING_HOST_HEARTBEAT_SENDER_H_ 151