blob: 167b5e1bd43f6ba2e9abbabf2acd1d010058aa4a [file] [log] [blame]
[email protected]988dfc3c2012-01-04 01:10:111// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]cb3b1f9312010-06-07 19:58:232// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "remoting/host/heartbeat_sender.h"
6
[email protected]da32a462012-02-08 01:43:267#include <math.h>
8
[email protected]41ed1312011-08-26 00:03:479#include "base/bind.h"
[email protected]cb3b1f9312010-06-07 19:58:2310#include "base/logging.h"
[email protected]60fc96002011-08-12 23:07:0511#include "base/message_loop_proxy.h"
[email protected]da32a462012-02-08 01:43:2612#include "base/rand_util.h"
[email protected]cdf8c572010-08-04 23:04:0513#include "base/string_number_conversions.h"
14#include "base/time.h"
[email protected]cb3b1f9312010-06-07 19:58:2315#include "remoting/base/constants.h"
[email protected]bd7b911d2012-05-22 16:44:1216#include "remoting/host/server_log_entry.h"
[email protected]b39e1822011-11-04 01:00:4917#include "remoting/jingle_glue/iq_sender.h"
[email protected]b6e2d6a62011-06-29 22:31:0418#include "remoting/jingle_glue/signal_strategy.h"
[email protected]cdf8c572010-08-04 23:04:0519#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
20#include "third_party/libjingle/source/talk/xmpp/constants.h"
[email protected]cb3b1f9312010-06-07 19:58:2321
[email protected]0e097f902010-12-14 03:05:4022using buzz::QName;
23using buzz::XmlElement;
24
[email protected]cb3b1f9312010-06-07 19:58:2325namespace remoting {
26
27namespace {
[email protected]b5a6afe2012-01-07 05:48:2028
[email protected]cdf8c572010-08-04 23:04:0529const char kHeartbeatQueryTag[] = "heartbeat";
30const char kHostIdAttr[] = "hostid";
31const char kHeartbeatSignatureTag[] = "signature";
[email protected]da32a462012-02-08 01:43:2632const char kSequenceIdAttr[] = "sequence-id";
[email protected]cb3b1f9312010-06-07 19:58:2333
[email protected]f43970a2012-02-01 04:22:4334const char kErrorTag[] = "error";
35const char kNotFoundTag[] = "item-not-found";
36
[email protected]0e097f902010-12-14 03:05:4037const char kHeartbeatResultTag[] = "heartbeat-result";
38const char kSetIntervalTag[] = "set-interval";
[email protected]da32a462012-02-08 01:43:2639const char kExpectedSequenceIdTag[] = "expected-sequence-id";
[email protected]0e097f902010-12-14 03:05:4040
41const int64 kDefaultHeartbeatIntervalMs = 5 * 60 * 1000; // 5 minutes.
[email protected]da32a462012-02-08 01:43:2642const int64 kResendDelayMs = 10 * 1000; // 10 seconds.
[email protected]67d948d02012-04-12 03:56:3943const int64 kResendDelayOnHostNotFoundMs = 10 * 1000; // 10 seconds.
44const int kMaxResendOnHostNotFoundCount = 12; // 2 minutes (12 x 10 seconds).
[email protected]cb3b1f9312010-06-07 19:58:2345
[email protected]b5a6afe2012-01-07 05:48:2046} // namespace
47
48HeartbeatSender::HeartbeatSender(
[email protected]ac31b782012-04-17 22:30:5449 Listener* listener,
[email protected]b5a6afe2012-01-07 05:48:2050 const std::string& host_id,
51 SignalStrategy* signal_strategy,
52 HostKeyPair* key_pair)
[email protected]ac31b782012-04-17 22:30:5453 : listener_(listener),
54 host_id_(host_id),
[email protected]b5a6afe2012-01-07 05:48:2055 signal_strategy_(signal_strategy),
56 key_pair_(key_pair),
[email protected]da32a462012-02-08 01:43:2657 interval_ms_(kDefaultHeartbeatIntervalMs),
58 sequence_id_(0),
59 sequence_id_was_set_(false),
[email protected]67d948d02012-04-12 03:56:3960 sequence_id_recent_set_num_(0),
61 heartbeat_succeeded_(false),
62 failed_startup_heartbeat_count_(0) {
[email protected]b5a6afe2012-01-07 05:48:2063 DCHECK(signal_strategy_);
64 DCHECK(key_pair_);
65
66 signal_strategy_->AddListener(this);
67
68 // Start heartbeats if the |signal_strategy_| is already connected.
69 OnSignalStrategyStateChange(signal_strategy_->GetState());
[email protected]cb3b1f9312010-06-07 19:58:2370}
71
[email protected]cdf8c572010-08-04 23:04:0572HeartbeatSender::~HeartbeatSender() {
[email protected]b5a6afe2012-01-07 05:48:2073 signal_strategy_->RemoveListener(this);
[email protected]cdf8c572010-08-04 23:04:0574}
75
[email protected]988dfc3c2012-01-04 01:10:1176void HeartbeatSender::OnSignalStrategyStateChange(SignalStrategy::State state) {
77 if (state == SignalStrategy::CONNECTED) {
[email protected]988dfc3c2012-01-04 01:10:1178 iq_sender_.reset(new IqSender(signal_strategy_));
[email protected]da32a462012-02-08 01:43:2679 SendStanza();
[email protected]988dfc3c2012-01-04 01:10:1180 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(interval_ms_),
[email protected]da32a462012-02-08 01:43:2681 this, &HeartbeatSender::SendStanza);
[email protected]988dfc3c2012-01-04 01:10:1182 } else if (state == SignalStrategy::DISCONNECTED) {
[email protected]988dfc3c2012-01-04 01:10:1183 request_.reset();
84 iq_sender_.reset();
85 timer_.Stop();
[email protected]da32a462012-02-08 01:43:2686 timer_resend_.Stop();
[email protected]988dfc3c2012-01-04 01:10:1187 }
[email protected]cb3b1f9312010-06-07 19:58:2388}
89
[email protected]c172d6972012-08-09 22:18:2190bool HeartbeatSender::OnSignalStrategyIncomingStanza(
91 const buzz::XmlElement* stanza) {
92 return false;
93}
94
[email protected]da32a462012-02-08 01:43:2695void HeartbeatSender::SendStanza() {
96 DoSendStanza();
97 // Make sure we don't send another heartbeat before the heartbeat interval
98 // has expired.
99 timer_resend_.Stop();
100}
101
102void HeartbeatSender::ResendStanza() {
103 DoSendStanza();
104 // Make sure we don't send another heartbeat before the heartbeat interval
105 // has expired.
106 timer_.Reset();
107}
108
[email protected]cb3b1f9312010-06-07 19:58:23109void HeartbeatSender::DoSendStanza() {
[email protected]f6bca1f2011-05-03 20:07:40110 VLOG(1) << "Sending heartbeat stanza to " << kChromotingBotJid;
[email protected]cff27642012-02-23 12:06:19111 request_ = iq_sender_->SendIq(
[email protected]b39e1822011-11-04 01:00:49112 buzz::STR_SET, kChromotingBotJid, CreateHeartbeatMessage(),
113 base::Bind(&HeartbeatSender::ProcessResponse,
[email protected]cff27642012-02-23 12:06:19114 base::Unretained(this)));
[email protected]da32a462012-02-08 01:43:26115 ++sequence_id_;
[email protected]cb3b1f9312010-06-07 19:58:23116}
117
[email protected]137e7cd2012-02-26 00:28:07118void HeartbeatSender::ProcessResponse(IqRequest* request,
119 const XmlElement* response) {
[email protected]0e097f902010-12-14 03:05:40120 std::string type = response->Attr(buzz::QN_TYPE);
121 if (type == buzz::STR_ERROR) {
[email protected]f43970a2012-02-01 04:22:43122 const XmlElement* error_element =
123 response->FirstNamed(QName(buzz::NS_CLIENT, kErrorTag));
124 if (error_element) {
125 if (error_element->FirstNamed(QName(buzz::NS_STANZA, kNotFoundTag))) {
[email protected]67d948d02012-04-12 03:56:39126 LOG(ERROR) << "Received error: Host ID not found";
127 // If the host was registered immediately before it sends a heartbeat,
128 // then server-side latency may prevent the server recognizing the
129 // host ID in the heartbeat. So even if all of the first few heartbeats
130 // get a "host ID not found" error, that's not a good enough reason to
131 // exit.
132 failed_startup_heartbeat_count_++;
133 if (!heartbeat_succeeded_ && (failed_startup_heartbeat_count_ <=
134 kMaxResendOnHostNotFoundCount)) {
135 timer_resend_.Start(FROM_HERE,
136 base::TimeDelta::FromMilliseconds(
137 kResendDelayOnHostNotFoundMs),
138 this,
139 &HeartbeatSender::ResendStanza);
140 return;
141 }
[email protected]ac31b782012-04-17 22:30:54142 listener_->OnUnknownHostIdError();
143 return;
[email protected]f43970a2012-02-01 04:22:43144 }
145 }
146
[email protected]95def6d2010-06-08 01:50:41147 LOG(ERROR) << "Received error in response to heartbeat: "
148 << response->Str();
[email protected]0e097f902010-12-14 03:05:40149 return;
150 }
151
[email protected]67d948d02012-04-12 03:56:39152 heartbeat_succeeded_ = true;
153
[email protected]0e097f902010-12-14 03:05:40154 // This method must only be called for error or result stanzas.
[email protected]0116cf52011-11-01 02:44:32155 DCHECK_EQ(std::string(buzz::STR_RESULT), type);
[email protected]0e097f902010-12-14 03:05:40156
157 const XmlElement* result_element =
158 response->FirstNamed(QName(kChromotingXmlNamespace, kHeartbeatResultTag));
159 if (result_element) {
160 const XmlElement* set_interval_element =
161 result_element->FirstNamed(QName(kChromotingXmlNamespace,
162 kSetIntervalTag));
163 if (set_interval_element) {
164 const std::string& interval_str = set_interval_element->BodyText();
165 int interval;
166 if (!base::StringToInt(interval_str, &interval) || interval <= 0) {
167 LOG(ERROR) << "Received invalid set-interval: "
168 << set_interval_element->Str();
169 } else {
[email protected]f6bca1f2011-05-03 20:07:40170 SetInterval(interval * base::Time::kMillisecondsPerSecond);
[email protected]0e097f902010-12-14 03:05:40171 }
172 }
[email protected]da32a462012-02-08 01:43:26173
174 bool did_set_sequence_id = false;
175 const XmlElement* expected_sequence_id_element =
176 result_element->FirstNamed(QName(kChromotingXmlNamespace,
177 kExpectedSequenceIdTag));
178 if (expected_sequence_id_element) {
179 // The sequence ID sent in the previous heartbeat was not what the server
180 // expected, so send another heartbeat with the expected sequence ID.
181 const std::string& expected_sequence_id_str =
182 expected_sequence_id_element->BodyText();
183 int expected_sequence_id;
184 if (!base::StringToInt(expected_sequence_id_str, &expected_sequence_id)) {
185 LOG(ERROR) << "Received invalid " << kExpectedSequenceIdTag << ": " <<
186 expected_sequence_id_element->Str();
187 } else {
188 SetSequenceId(expected_sequence_id);
189 sequence_id_recent_set_num_++;
190 did_set_sequence_id = true;
191 }
192 }
193 if (!did_set_sequence_id) {
194 sequence_id_recent_set_num_ = 0;
195 }
[email protected]95def6d2010-06-08 01:50:41196 }
197}
198
[email protected]f6bca1f2011-05-03 20:07:40199void HeartbeatSender::SetInterval(int interval) {
200 if (interval != interval_ms_) {
201 interval_ms_ = interval;
202
203 // Restart the timer with the new interval.
[email protected]b5a6afe2012-01-07 05:48:20204 if (timer_.IsRunning()) {
[email protected]f6bca1f2011-05-03 20:07:40205 timer_.Stop();
[email protected]d323a172011-09-02 18:23:02206 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(interval_ms_),
[email protected]da32a462012-02-08 01:43:26207 this, &HeartbeatSender::SendStanza);
[email protected]f6bca1f2011-05-03 20:07:40208 }
209 }
210}
211
[email protected]da32a462012-02-08 01:43:26212void HeartbeatSender::SetSequenceId(int sequence_id) {
213 sequence_id_ = sequence_id;
214 // Setting the sequence ID may be a symptom of a temporary server-side
215 // problem, which would affect many hosts, so don't send a new heartbeat
216 // immediately, as many hosts doing so may overload the server.
217 // But the server will usually set the sequence ID when it receives the first
218 // heartbeat from a host. In that case, we can send a new heartbeat
219 // immediately, as that only happens once per host instance.
220 if (!sequence_id_was_set_) {
221 ResendStanza();
222 } else {
223 LOG(INFO) << "The heartbeat sequence ID has been set more than once: "
224 << "the new value is " << sequence_id;
225 double delay = pow(2.0, sequence_id_recent_set_num_) *
226 (1 + base::RandDouble()) * kResendDelayMs;
227 if (delay <= interval_ms_) {
228 timer_resend_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(delay),
229 this, &HeartbeatSender::ResendStanza);
230 }
231 }
232 sequence_id_was_set_ = true;
233}
234
[email protected]cff27642012-02-23 12:06:19235scoped_ptr<XmlElement> HeartbeatSender::CreateHeartbeatMessage() {
[email protected]bd7b911d2012-05-22 16:44:12236 // Create heartbeat stanza.
[email protected]cff27642012-02-23 12:06:19237 scoped_ptr<XmlElement> query(new XmlElement(
238 QName(kChromotingXmlNamespace, kHeartbeatQueryTag)));
[email protected]0e097f902010-12-14 03:05:40239 query->AddAttr(QName(kChromotingXmlNamespace, kHostIdAttr), host_id_);
[email protected]da32a462012-02-08 01:43:26240 query->AddAttr(QName(kChromotingXmlNamespace, kSequenceIdAttr),
241 base::IntToString(sequence_id_));
[email protected]cff27642012-02-23 12:06:19242 query->AddElement(CreateSignature().release());
[email protected]bd7b911d2012-05-22 16:44:12243 // Append log message (which isn't signed).
244 scoped_ptr<XmlElement> log(ServerLogEntry::MakeStanza());
245 scoped_ptr<ServerLogEntry> log_entry(ServerLogEntry::MakeForHeartbeat());
246 log_entry->AddHostFields();
247 log->AddElement(log_entry->ToStanza().release());
248 query->AddElement(log.release());
[email protected]cff27642012-02-23 12:06:19249 return query.Pass();
[email protected]cdf8c572010-08-04 23:04:05250}
251
[email protected]cff27642012-02-23 12:06:19252scoped_ptr<XmlElement> HeartbeatSender::CreateSignature() {
253 scoped_ptr<XmlElement> signature_tag(new XmlElement(
254 QName(kChromotingXmlNamespace, kHeartbeatSignatureTag)));
[email protected]cdf8c572010-08-04 23:04:05255
[email protected]da32a462012-02-08 01:43:26256 std::string message = signal_strategy_->GetLocalJid() + ' ' +
257 base::IntToString(sequence_id_);
[email protected]b5a6afe2012-01-07 05:48:20258 std::string signature(key_pair_->GetSignature(message));
[email protected]cdf8c572010-08-04 23:04:05259 signature_tag->AddText(signature);
260
[email protected]cff27642012-02-23 12:06:19261 return signature_tag.Pass();
[email protected]cdf8c572010-08-04 23:04:05262}
263
[email protected]cb3b1f9312010-06-07 19:58:23264} // namespace remoting