| // 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. |
| |
| #ifndef REMOTING_HOST_HEARTBEAT_SENDER_H_ |
| #define REMOTING_HOST_HEARTBEAT_SENDER_H_ |
| |
| #include <string> |
| |
| #include "base/compiler_specific.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/timer/timer.h" |
| #include "remoting/base/rsa_key_pair.h" |
| #include "remoting/jingle_glue/signal_strategy.h" |
| |
| namespace base { |
| class MessageLoopProxy; |
| } // namespace base |
| |
| namespace buzz { |
| class XmlElement; |
| } // namespace buzz |
| |
| namespace remoting { |
| |
| class RsaKeyPair; |
| class IqRequest; |
| class IqSender; |
| |
| // HeartbeatSender periodically sends heartbeat stanzas to the Chromoting Bot. |
| // Each heartbeat stanza looks as follows: |
| // |
| // <iq type="set" to="remoting@bot.talk.google.com" |
| // from="user@gmail.com/chromoting123123" id="5" xmlns="jabber:client"> |
| // <rem:heartbeat rem:hostid="a1ddb11e-8aef-11df-bccf-18a905b9cb5a" |
| // rem:sequence-id="456" |
| // xmlns:rem="google:remoting"> |
| // <rem:signature>.signature.</rem:signature> |
| // </rem:heartbeat> |
| // </iq> |
| // |
| // The sequence-id attribute of the heartbeat is a zero-based incrementally |
| // increasing integer unique to each heartbeat from a single host. |
| // The Bot checks the value, and if it is incorrect, includes the |
| // correct value in the result stanza. The host should then send another |
| // heartbeat, with the correct sequence-id, and increment the sequence-id in |
| // susbequent heartbeats. |
| // The signature is a base-64 encoded SHA-1 hash, signed with the host's |
| // private RSA key. The message being signed is the full Jid concatenated with |
| // the sequence-id, separated by one space. For example, for the heartbeat |
| // stanza above, the message that is signed is |
| // "user@gmail.com/chromoting123123 456". |
| // |
| // The Bot sends the following result stanza in response to each successful |
| // 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-result> |
| // </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. |
| // If the heartbeat's sequence-id was incorrect, the Bot sends a result |
| // stanza of this form: |
| // |
| // <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:expected-sequence-id>654</rem:expected-sequence-id> |
| // </rem:heartbeat-result> |
| // </iq> |
| class HeartbeatSender : public SignalStrategy::Listener { |
| public: |
| class Listener { |
| public: |
| virtual ~Listener() { } |
| |
| // Invoked after the first successful heartbeat. |
| virtual void OnHeartbeatSuccessful() = 0; |
| |
| // Invoked when the host ID is permanently not recognized by the server. |
| virtual void OnUnknownHostIdError() = 0; |
| }; |
| |
| // |signal_strategy| and |delegate| must outlive this |
| // object. Heartbeats will start when the supplied SignalStrategy |
| // enters the CONNECTED state. |
| HeartbeatSender(Listener* listener, |
| const std::string& host_id, |
| SignalStrategy* signal_strategy, |
| scoped_refptr<RsaKeyPair> key_pair, |
| const std::string& directory_bot_jid); |
| virtual ~HeartbeatSender(); |
| |
| // SignalStrategy::Listener interface. |
| virtual void OnSignalStrategyStateChange( |
| SignalStrategy::State state) OVERRIDE; |
| virtual bool OnSignalStrategyIncomingStanza( |
| const buzz::XmlElement* stanza) OVERRIDE; |
| |
| private: |
| FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, DoSendStanza); |
| FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, |
| DoSendStanzaWithExpectedSequenceId); |
| FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, CreateHeartbeatMessage); |
| FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, ProcessResponseSetInterval); |
| FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, |
| ProcessResponseExpectedSequenceId); |
| |
| void SendStanza(); |
| void ResendStanza(); |
| void DoSendStanza(); |
| void ProcessResponse(IqRequest* request, const buzz::XmlElement* response); |
| void SetInterval(int interval); |
| void SetSequenceId(int sequence_id); |
| |
| // Helper methods used by DoSendStanza() to generate heartbeat stanzas. |
| scoped_ptr<buzz::XmlElement> CreateHeartbeatMessage(); |
| scoped_ptr<buzz::XmlElement> CreateSignature(); |
| |
| Listener* listener_; |
| std::string host_id_; |
| SignalStrategy* signal_strategy_; |
| scoped_refptr<RsaKeyPair> key_pair_; |
| std::string directory_bot_jid_; |
| scoped_ptr<IqSender> iq_sender_; |
| scoped_ptr<IqRequest> request_; |
| int interval_ms_; |
| base::RepeatingTimer<HeartbeatSender> timer_; |
| base::OneShotTimer<HeartbeatSender> timer_resend_; |
| int sequence_id_; |
| bool sequence_id_was_set_; |
| int sequence_id_recent_set_num_; |
| bool heartbeat_succeeded_; |
| int failed_startup_heartbeat_count_; |
| |
| DISALLOW_COPY_AND_ASSIGN(HeartbeatSender); |
| }; |
| |
| } // namespace remoting |
| |
| #endif // REMOTING_HOST_HEARTBEAT_SENDER_H_ |