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/callback.h"
11 #include "base/compiler_specific.h"
12 #include "base/gtest_prod_util.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/threading/thread_checker.h"
16 #include "base/timer/timer.h"
17 #include "remoting/base/rsa_key_pair.h"
18 #include "remoting/signaling/signal_strategy.h"
21 class MessageLoopProxy
;
35 // HeartbeatSender periodically sends heartbeat stanzas to the Chromoting Bot.
36 // Each heartbeat stanza looks as follows:
38 // <iq type="set" to="remoting@bot.talk.google.com"
39 // from="user@gmail.com/chromoting123123" id="5" xmlns="jabber:client">
40 // <rem:heartbeat rem:hostid="a1ddb11e-8aef-11df-bccf-18a905b9cb5a"
41 // rem:sequence-id="456"
42 // xmlns:rem="google:remoting">
43 // <rem:signature>.signature.</rem:signature>
47 // Normally the heartbeat indicates that the host is healthy and ready to
48 // accept new connections from a client, but the rem:heartbeat xml element can
49 // optionally include a rem:host-offline-reason attribute, which indicates that
50 // the host cannot accept connections from the client (and might possibly be
51 // shutting down). The value of the host-offline-reason attribute can be a
52 // string from host_exit_codes.cc (i.e. "INVALID_HOST_CONFIGURATION" string).
54 // The sequence-id attribute of the heartbeat is a zero-based incrementally
55 // increasing integer unique to each heartbeat from a single host.
56 // The Bot checks the value, and if it is incorrect, includes the
57 // correct value in the result stanza. The host should then send another
58 // heartbeat, with the correct sequence-id, and increment the sequence-id in
59 // susbequent heartbeats.
60 // The signature is a base-64 encoded SHA-1 hash, signed with the host's
61 // private RSA key. The message being signed is the full Jid concatenated with
62 // the sequence-id, separated by one space. For example, for the heartbeat
63 // stanza above, the message that is signed is
64 // "user@gmail.com/chromoting123123 456".
66 // The Bot sends the following result stanza in response to each successful
69 // <iq type="set" from="remoting@bot.talk.google.com"
70 // to="user@gmail.com/chromoting123123" id="5" xmlns="jabber:client">
71 // <rem:heartbeat-result xmlns:rem="google:remoting">
72 // <rem:set-interval>300</rem:set-interval>
73 // </rem:heartbeat-result>
76 // The set-interval tag is used to specify desired heartbeat interval
77 // in seconds. The heartbeat-result and the set-interval tags are
78 // optional. Host uses default heartbeat interval if it doesn't find
79 // set-interval tag in the result Iq stanza it receives from the
81 // If the heartbeat's sequence-id was incorrect, the Bot sends a result
82 // stanza of this form:
84 // <iq type="set" from="remoting@bot.talk.google.com"
85 // to="user@gmail.com/chromoting123123" id="5" xmlns="jabber:client">
86 // <rem:heartbeat-result xmlns:rem="google:remoting">
87 // <rem:expected-sequence-id>654</rem:expected-sequence-id>
88 // </rem:heartbeat-result>
90 class HeartbeatSender
: public SignalStrategy::Listener
{
92 // |signal_strategy| and |delegate| must outlive this
93 // object. Heartbeats will start when the supplied SignalStrategy
94 // enters the CONNECTED state.
96 // |on_heartbeat_successful_callback| is invoked after the first successful
99 // |on_unknown_host_id_error| is invoked when the host ID is permanently not
100 // recognized by the server.
101 HeartbeatSender(const base::Closure
& on_heartbeat_successful_callback
,
102 const base::Closure
& on_unknown_host_id_error
,
103 const std::string
& host_id
,
104 SignalStrategy
* signal_strategy
,
105 const scoped_refptr
<const RsaKeyPair
>& host_key_pair
,
106 const std::string
& directory_bot_jid
);
107 ~HeartbeatSender() override
;
109 // Sets host offline reason for future heartbeat stanzas, and initiates
110 // sending a stanza right away.
112 // For discussion of allowed values for |host_offline_reason| argument,
113 // please see the description of rem:host-offline-reason xml attribute in
114 // the class-level comments above.
116 // |ack_callback| will be called once, when the bot acks receiving the
117 // |host_offline_reason| or when |timeout| is reached.
118 void SetHostOfflineReason(
119 const std::string
& host_offline_reason
,
120 const base::TimeDelta
& timeout
,
121 const base::Callback
<void(bool success
)>& ack_callback
);
123 // SignalStrategy::Listener interface.
124 void OnSignalStrategyStateChange(SignalStrategy::State state
) override
;
125 bool OnSignalStrategyIncomingStanza(const buzz::XmlElement
* stanza
) override
;
128 FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest
,
129 DoSendStanzaWithExpectedSequenceId
);
130 FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest
, ProcessResponseSetInterval
);
131 FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest
,
132 ProcessResponseExpectedSequenceId
);
133 friend class HeartbeatSenderTest
;
138 void ProcessResponse(bool is_offline_heartbeat_response
,
140 const buzz::XmlElement
* response
);
141 void SetInterval(int interval
);
142 void SetSequenceId(int sequence_id
);
144 // Handlers for host-offline-reason completion and timeout.
145 void OnHostOfflineReasonTimeout();
146 void OnHostOfflineReasonAck();
148 // Helper methods used by DoSendStanza() to generate heartbeat stanzas.
149 scoped_ptr
<buzz::XmlElement
> CreateHeartbeatMessage();
150 scoped_ptr
<buzz::XmlElement
> CreateSignature();
152 base::Closure on_heartbeat_successful_callback_
;
153 base::Closure on_unknown_host_id_error_
;
154 std::string host_id_
;
155 SignalStrategy
* signal_strategy_
;
156 scoped_refptr
<const RsaKeyPair
> host_key_pair_
;
157 std::string directory_bot_jid_
;
158 scoped_ptr
<IqSender
> iq_sender_
;
159 scoped_ptr
<IqRequest
> request_
;
161 base::RepeatingTimer
<HeartbeatSender
> timer_
;
162 base::OneShotTimer
<HeartbeatSender
> timer_resend_
;
164 bool sequence_id_was_set_
;
165 int sequence_id_recent_set_num_
;
166 bool heartbeat_succeeded_
;
167 int failed_startup_heartbeat_count_
;
169 // Fields to send and indicate completion of sending host-offline-reason.
170 std::string host_offline_reason_
;
171 base::Callback
<void(bool success
)> host_offline_reason_ack_callback_
;
172 base::OneShotTimer
<HeartbeatSender
> host_offline_reason_timeout_timer_
;
174 base::ThreadChecker thread_checker_
;
176 DISALLOW_COPY_AND_ASSIGN(HeartbeatSender
);
179 } // namespace remoting
181 #endif // REMOTING_HOST_HEARTBEAT_SENDER_H_