Home | History | Annotate | Download | only in host
      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