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 18 namespace base { 19 class MessageLoopProxy; 20 } // namespace base 21 22 namespace buzz { 23 class XmlElement; 24 } // namespace buzz 25 26 namespace remoting { 27 28 class RsaKeyPair; 29 class IqRequest; 30 class 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 (at) bot.talk.google.com" 36 // from="user (at) 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 (at) 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 (at) bot.talk.google.com" 60 // to="user (at) 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 (at) bot.talk.google.com" 75 // to="user (at) 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> 80 class 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