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.
5 #ifndef REMOTING_HOST_HEARTBEAT_SENDER_H_
6 #define REMOTING_HOST_HEARTBEAT_SENDER_H_
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"
19 class MessageLoopProxy
;
32 // HeartbeatSender periodically sends heartbeat stanzas to the Chromoting Bot.
33 // Each heartbeat stanza looks as follows:
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>
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".
56 // The Bot sends the following result stanza in response to each successful
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>
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
71 // If the heartbeat's sequence-id was incorrect, the Bot sends a result
72 // stanza of this form:
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>
80 class HeartbeatSender
: public SignalStrategy::Listener
{
84 virtual ~Listener() { }
86 // Invoked after the first successful heartbeat.
87 virtual void OnHeartbeatSuccessful() = 0;
89 // Invoked when the host ID is permanently not recognized by the server.
90 virtual void OnUnknownHostIdError() = 0;
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();
103 // SignalStrategy::Listener interface.
104 virtual void OnSignalStrategyStateChange(
105 SignalStrategy::State state
) OVERRIDE
;
106 virtual bool OnSignalStrategyIncomingStanza(
107 const buzz::XmlElement
* stanza
) OVERRIDE
;
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
);
121 void ProcessResponse(IqRequest
* request
, const buzz::XmlElement
* response
);
122 void SetInterval(int interval
);
123 void SetSequenceId(int sequence_id
);
125 // Helper methods used by DoSendStanza() to generate heartbeat stanzas.
126 scoped_ptr
<buzz::XmlElement
> CreateHeartbeatMessage();
127 scoped_ptr
<buzz::XmlElement
> CreateSignature();
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_
;
137 base::RepeatingTimer
<HeartbeatSender
> timer_
;
138 base::OneShotTimer
<HeartbeatSender
> timer_resend_
;
140 bool sequence_id_was_set_
;
141 int sequence_id_recent_set_num_
;
142 bool heartbeat_succeeded_
;
143 int failed_startup_heartbeat_count_
;
145 DISALLOW_COPY_AND_ASSIGN(HeartbeatSender
);
148 } // namespace remoting
150 #endif // REMOTING_HOST_HEARTBEAT_SENDER_H_