Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "remoting/host/heartbeat_sender.h" | 5 #include "remoting/host/heartbeat_sender.h" |
| 6 | 6 |
| 7 #include <math.h> | 7 #include <math.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 41 const char kHostOperatingSystemNameTag[] = "host-os-name"; | 41 const char kHostOperatingSystemNameTag[] = "host-os-name"; |
| 42 const char kHostOperatingSystemVersionTag[] = "host-os-version"; | 42 const char kHostOperatingSystemVersionTag[] = "host-os-version"; |
| 43 | 43 |
| 44 const char kErrorTag[] = "error"; | 44 const char kErrorTag[] = "error"; |
| 45 const char kNotFoundTag[] = "item-not-found"; | 45 const char kNotFoundTag[] = "item-not-found"; |
| 46 | 46 |
| 47 const char kHeartbeatResultTag[] = "heartbeat-result"; | 47 const char kHeartbeatResultTag[] = "heartbeat-result"; |
| 48 const char kSetIntervalTag[] = "set-interval"; | 48 const char kSetIntervalTag[] = "set-interval"; |
| 49 const char kExpectedSequenceIdTag[] = "expected-sequence-id"; | 49 const char kExpectedSequenceIdTag[] = "expected-sequence-id"; |
| 50 | 50 |
| 51 const int64_t kDefaultHeartbeatIntervalMs = 5 * 60 * 1000; // 5 minutes. | 51 constexpr base::TimeDelta kDefaultHeartbeatInterval = |
| 52 const int64_t kResendDelayMs = 10 * 1000; // 10 seconds. | 52 base::TimeDelta::FromMinutes(5); |
| 53 const int64_t kResendDelayOnHostNotFoundMs = 10 * 1000; // 10 seconds. | 53 constexpr base::TimeDelta kHeartbeatResponseTimeout = |
| 54 base::TimeDelta::FromSeconds(10); | |
|
Jamie
2017/06/02 00:53:52
This seems a bit on the low side. IIRC, TCP timeou
Sergey Ulanov
2017/06/02 18:37:01
Done.
| |
| 55 constexpr base::TimeDelta kResendDelay = base::TimeDelta::FromSeconds(10); | |
| 56 constexpr base::TimeDelta kResendDelayOnHostNotFound = | |
| 57 base::TimeDelta::FromSeconds(10); | |
| 58 | |
| 54 const int kMaxResendOnHostNotFoundCount = 12; // 2 minutes (12 x 10 seconds). | 59 const int kMaxResendOnHostNotFoundCount = 12; // 2 minutes (12 x 10 seconds). |
| 60 const int kMaxHeartbeatTimeouts = 2; | |
| 55 | 61 |
| 56 } // namespace | 62 } // namespace |
| 57 | 63 |
| 58 HeartbeatSender::HeartbeatSender( | 64 HeartbeatSender::HeartbeatSender( |
| 59 const base::Closure& on_heartbeat_successful_callback, | 65 const base::Closure& on_heartbeat_successful_callback, |
| 60 const base::Closure& on_unknown_host_id_error, | 66 const base::Closure& on_unknown_host_id_error, |
| 61 const std::string& host_id, | 67 const std::string& host_id, |
| 62 SignalStrategy* signal_strategy, | 68 SignalStrategy* signal_strategy, |
| 63 const scoped_refptr<const RsaKeyPair>& host_key_pair, | 69 const scoped_refptr<const RsaKeyPair>& host_key_pair, |
| 64 const std::string& directory_bot_jid) | 70 const std::string& directory_bot_jid) |
| 65 : on_heartbeat_successful_callback_(on_heartbeat_successful_callback), | 71 : on_heartbeat_successful_callback_(on_heartbeat_successful_callback), |
| 66 on_unknown_host_id_error_(on_unknown_host_id_error), | 72 on_unknown_host_id_error_(on_unknown_host_id_error), |
| 67 host_id_(host_id), | 73 host_id_(host_id), |
| 68 signal_strategy_(signal_strategy), | 74 signal_strategy_(signal_strategy), |
| 69 host_key_pair_(host_key_pair), | 75 host_key_pair_(host_key_pair), |
| 70 directory_bot_jid_(directory_bot_jid), | 76 directory_bot_jid_(directory_bot_jid), |
| 71 interval_ms_(kDefaultHeartbeatIntervalMs), | 77 interval_(kDefaultHeartbeatInterval) { |
| 72 sequence_id_(0), | |
| 73 sequence_id_was_set_(false), | |
| 74 sequence_id_recent_set_num_(0), | |
| 75 heartbeat_succeeded_(false), | |
| 76 failed_startup_heartbeat_count_(0) { | |
| 77 DCHECK(signal_strategy_); | 78 DCHECK(signal_strategy_); |
| 78 DCHECK(host_key_pair_.get()); | 79 DCHECK(host_key_pair_.get()); |
| 79 DCHECK(thread_checker_.CalledOnValidThread()); | 80 DCHECK(thread_checker_.CalledOnValidThread()); |
| 80 | 81 |
| 81 signal_strategy_->AddListener(this); | 82 signal_strategy_->AddListener(this); |
| 82 | 83 |
| 83 // Start heartbeats if the |signal_strategy_| is already connected. | 84 // Start heartbeats if the |signal_strategy_| is already connected. |
| 84 OnSignalStrategyStateChange(signal_strategy_->GetState()); | 85 OnSignalStrategyStateChange(signal_strategy_->GetState()); |
| 85 } | 86 } |
| 86 | 87 |
| 87 HeartbeatSender::~HeartbeatSender() { | 88 HeartbeatSender::~HeartbeatSender() { |
| 88 DCHECK(thread_checker_.CalledOnValidThread()); | 89 DCHECK(thread_checker_.CalledOnValidThread()); |
| 89 signal_strategy_->RemoveListener(this); | 90 signal_strategy_->RemoveListener(this); |
| 90 } | 91 } |
| 91 | 92 |
| 92 void HeartbeatSender::OnSignalStrategyStateChange(SignalStrategy::State state) { | 93 void HeartbeatSender::OnSignalStrategyStateChange(SignalStrategy::State state) { |
| 93 DCHECK(thread_checker_.CalledOnValidThread()); | 94 DCHECK(thread_checker_.CalledOnValidThread()); |
| 94 if (state == SignalStrategy::CONNECTED) { | 95 if (state == SignalStrategy::CONNECTED) { |
| 95 iq_sender_.reset(new IqSender(signal_strategy_)); | 96 iq_sender_ = base::MakeUnique<IqSender>(signal_strategy_); |
| 96 SendStanza(); | 97 SendStanza(); |
| 97 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(interval_ms_), | 98 timer_.Start(FROM_HERE, interval_, this, &HeartbeatSender::SendStanza); |
| 98 this, &HeartbeatSender::SendStanza); | |
| 99 } else if (state == SignalStrategy::DISCONNECTED) { | 99 } else if (state == SignalStrategy::DISCONNECTED) { |
| 100 request_.reset(); | 100 request_.reset(); |
| 101 iq_sender_.reset(); | 101 iq_sender_.reset(); |
| 102 timer_.Stop(); | 102 timer_.Stop(); |
| 103 timer_resend_.Stop(); | 103 timer_resend_.Stop(); |
| 104 } | 104 } |
| 105 } | 105 } |
| 106 | 106 |
| 107 bool HeartbeatSender::OnSignalStrategyIncomingStanza( | 107 bool HeartbeatSender::OnSignalStrategyIncomingStanza( |
| 108 const buzz::XmlElement* stanza) { | 108 const buzz::XmlElement* stanza) { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 162 DoSendStanza(); | 162 DoSendStanza(); |
| 163 } | 163 } |
| 164 | 164 |
| 165 void HeartbeatSender::DoSendStanza() { | 165 void HeartbeatSender::DoSendStanza() { |
| 166 DCHECK(thread_checker_.CalledOnValidThread()); | 166 DCHECK(thread_checker_.CalledOnValidThread()); |
| 167 DCHECK(signal_strategy_->GetState() == SignalStrategy::CONNECTED); | 167 DCHECK(signal_strategy_->GetState() == SignalStrategy::CONNECTED); |
| 168 VLOG(1) << "Sending heartbeat stanza to " << directory_bot_jid_; | 168 VLOG(1) << "Sending heartbeat stanza to " << directory_bot_jid_; |
| 169 | 169 |
| 170 request_ = iq_sender_->SendIq( | 170 request_ = iq_sender_->SendIq( |
| 171 buzz::STR_SET, directory_bot_jid_, CreateHeartbeatMessage(), | 171 buzz::STR_SET, directory_bot_jid_, CreateHeartbeatMessage(), |
| 172 base::Bind(&HeartbeatSender::ProcessResponse, | 172 base::Bind(&HeartbeatSender::ProcessResponse, base::Unretained(this), |
| 173 base::Unretained(this), | |
| 174 !host_offline_reason_.empty())); | 173 !host_offline_reason_.empty())); |
| 174 request_->SetTimeout(kHeartbeatResponseTimeout); | |
| 175 ++sequence_id_; | 175 ++sequence_id_; |
| 176 } | 176 } |
| 177 | 177 |
| 178 void HeartbeatSender::ProcessResponse( | 178 void HeartbeatSender::ProcessResponse(bool is_offline_heartbeat_response, |
| 179 bool is_offline_heartbeat_response, | 179 IqRequest* request, |
| 180 IqRequest* request, | 180 const XmlElement* response) { |
| 181 const XmlElement* response) { | |
| 182 DCHECK(thread_checker_.CalledOnValidThread()); | 181 DCHECK(thread_checker_.CalledOnValidThread()); |
| 183 | 182 |
| 183 if (!response) { | |
| 184 timed_out_heartbeats_count_++; | |
| 185 if (timed_out_heartbeats_count_ >= kMaxHeartbeatTimeouts) { | |
| 186 LOG(ERROR) << "Heartbeat timed out. Reconnecting XMPP."; | |
| 187 timed_out_heartbeats_count_ = 0; | |
| 188 // SignalingConnector will reconnect SignalStrategy. | |
| 189 signal_strategy_->Disconnect(); | |
| 190 } else { | |
| 191 LOG(ERROR) << "Heartbeat timed out."; | |
| 192 } | |
| 193 return; | |
| 194 } else { | |
| 195 timed_out_heartbeats_count_ = 0; | |
| 196 } | |
| 197 | |
| 184 std::string type = response->Attr(buzz::QN_TYPE); | 198 std::string type = response->Attr(buzz::QN_TYPE); |
| 185 if (type == buzz::STR_ERROR) { | 199 if (type == buzz::STR_ERROR) { |
| 186 const XmlElement* error_element = | 200 const XmlElement* error_element = |
| 187 response->FirstNamed(QName(buzz::NS_CLIENT, kErrorTag)); | 201 response->FirstNamed(QName(buzz::NS_CLIENT, kErrorTag)); |
| 188 if (error_element) { | 202 if (error_element) { |
| 189 if (error_element->FirstNamed(QName(buzz::NS_STANZA, kNotFoundTag))) { | 203 if (error_element->FirstNamed(QName(buzz::NS_STANZA, kNotFoundTag))) { |
| 190 LOG(ERROR) << "Received error: Host ID not found"; | 204 LOG(ERROR) << "Received error: Host ID not found"; |
| 191 // If the host was registered immediately before it sends a heartbeat, | 205 // If the host was registered immediately before it sends a heartbeat, |
| 192 // then server-side latency may prevent the server recognizing the | 206 // then server-side latency may prevent the server recognizing the |
| 193 // host ID in the heartbeat. So even if all of the first few heartbeats | 207 // host ID in the heartbeat. So even if all of the first few heartbeats |
| 194 // get a "host ID not found" error, that's not a good enough reason to | 208 // get a "host ID not found" error, that's not a good enough reason to |
| 195 // exit. | 209 // exit. |
| 196 failed_startup_heartbeat_count_++; | 210 failed_startup_heartbeat_count_++; |
| 197 if (!heartbeat_succeeded_ && (failed_startup_heartbeat_count_ <= | 211 if (!heartbeat_succeeded_ && (failed_startup_heartbeat_count_ <= |
| 198 kMaxResendOnHostNotFoundCount)) { | 212 kMaxResendOnHostNotFoundCount)) { |
| 199 timer_resend_.Start(FROM_HERE, | 213 timer_resend_.Start(FROM_HERE, kResendDelayOnHostNotFound, this, |
| 200 base::TimeDelta::FromMilliseconds( | |
| 201 kResendDelayOnHostNotFoundMs), | |
| 202 this, | |
| 203 &HeartbeatSender::ResendStanza); | 214 &HeartbeatSender::ResendStanza); |
| 204 return; | 215 return; |
| 205 } | 216 } |
| 206 on_unknown_host_id_error_.Run(); | 217 on_unknown_host_id_error_.Run(); |
| 207 return; | 218 return; |
| 208 } | 219 } |
| 209 } | 220 } |
| 210 | 221 |
| 211 LOG(ERROR) << "Received error in response to heartbeat: " | 222 LOG(ERROR) << "Received error in response to heartbeat: " |
| 212 << response->Str(); | 223 << response->Str(); |
| 213 return; | 224 return; |
| 214 } | 225 } |
| 215 | 226 |
| 216 // This method must only be called for error or result stanzas. | 227 // This method must only be called for error or result stanzas. |
| 217 DCHECK_EQ(std::string(buzz::STR_RESULT), type); | 228 DCHECK_EQ(std::string(buzz::STR_RESULT), type); |
| 218 | 229 |
| 219 const XmlElement* result_element = | 230 const XmlElement* result_element = |
| 220 response->FirstNamed(QName(kChromotingXmlNamespace, kHeartbeatResultTag)); | 231 response->FirstNamed(QName(kChromotingXmlNamespace, kHeartbeatResultTag)); |
| 221 if (result_element) { | 232 if (result_element) { |
| 222 const XmlElement* set_interval_element = | 233 const XmlElement* set_interval_element = |
| 223 result_element->FirstNamed(QName(kChromotingXmlNamespace, | 234 result_element->FirstNamed(QName(kChromotingXmlNamespace, |
| 224 kSetIntervalTag)); | 235 kSetIntervalTag)); |
| 225 if (set_interval_element) { | 236 if (set_interval_element) { |
| 226 const std::string& interval_str = set_interval_element->BodyText(); | 237 const std::string& interval_str = set_interval_element->BodyText(); |
| 227 int interval; | 238 int interval_seconds; |
| 228 if (!base::StringToInt(interval_str, &interval) || interval <= 0) { | 239 if (!base::StringToInt(interval_str, &interval_seconds) || |
| 240 interval_seconds <= 0) { | |
| 229 LOG(ERROR) << "Received invalid set-interval: " | 241 LOG(ERROR) << "Received invalid set-interval: " |
| 230 << set_interval_element->Str(); | 242 << set_interval_element->Str(); |
| 231 } else { | 243 } else { |
| 232 SetInterval(interval * base::Time::kMillisecondsPerSecond); | 244 SetInterval(base::TimeDelta::FromSeconds(interval_seconds)); |
| 233 } | 245 } |
| 234 } | 246 } |
| 235 | 247 |
| 236 bool did_set_sequence_id = false; | 248 bool did_set_sequence_id = false; |
| 237 const XmlElement* expected_sequence_id_element = | 249 const XmlElement* expected_sequence_id_element = |
| 238 result_element->FirstNamed(QName(kChromotingXmlNamespace, | 250 result_element->FirstNamed(QName(kChromotingXmlNamespace, |
| 239 kExpectedSequenceIdTag)); | 251 kExpectedSequenceIdTag)); |
| 240 if (expected_sequence_id_element) { | 252 if (expected_sequence_id_element) { |
| 241 // The sequence ID sent in the previous heartbeat was not what the server | 253 // The sequence ID sent in the previous heartbeat was not what the server |
| 242 // expected, so send another heartbeat with the expected sequence ID. | 254 // expected, so send another heartbeat with the expected sequence ID. |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 263 heartbeat_succeeded_ = true; | 275 heartbeat_succeeded_ = true; |
| 264 | 276 |
| 265 // Notify caller of SetHostOfflineReason that we got an ack. | 277 // Notify caller of SetHostOfflineReason that we got an ack. |
| 266 if (is_offline_heartbeat_response) { | 278 if (is_offline_heartbeat_response) { |
| 267 OnHostOfflineReasonAck(); | 279 OnHostOfflineReasonAck(); |
| 268 } | 280 } |
| 269 } | 281 } |
| 270 } | 282 } |
| 271 } | 283 } |
| 272 | 284 |
| 273 void HeartbeatSender::SetInterval(int interval) { | 285 void HeartbeatSender::SetInterval(base::TimeDelta interval) { |
| 274 if (interval != interval_ms_) { | 286 if (interval != interval_) { |
| 275 interval_ms_ = interval; | 287 interval_ = interval; |
| 276 | 288 |
| 277 // Restart the timer with the new interval. | 289 // Restart the timer with the new interval. |
| 278 if (timer_.IsRunning()) { | 290 if (timer_.IsRunning()) { |
| 279 timer_.Stop(); | 291 timer_.Stop(); |
| 280 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(interval_ms_), | 292 timer_.Start(FROM_HERE, interval_, this, &HeartbeatSender::SendStanza); |
| 281 this, &HeartbeatSender::SendStanza); | |
| 282 } | 293 } |
| 283 } | 294 } |
| 284 } | 295 } |
| 285 | 296 |
| 286 void HeartbeatSender::SetSequenceId(int sequence_id) { | 297 void HeartbeatSender::SetSequenceId(int sequence_id) { |
| 287 sequence_id_ = sequence_id; | 298 sequence_id_ = sequence_id; |
| 288 // Setting the sequence ID may be a symptom of a temporary server-side | 299 // Setting the sequence ID may be a symptom of a temporary server-side |
| 289 // problem, which would affect many hosts, so don't send a new heartbeat | 300 // problem, which would affect many hosts, so don't send a new heartbeat |
| 290 // immediately, as many hosts doing so may overload the server. | 301 // immediately, as many hosts doing so may overload the server. |
| 291 // But the server will usually set the sequence ID when it receives the first | 302 // But the server will usually set the sequence ID when it receives the first |
| 292 // heartbeat from a host. In that case, we can send a new heartbeat | 303 // heartbeat from a host. In that case, we can send a new heartbeat |
| 293 // immediately, as that only happens once per host instance. | 304 // immediately, as that only happens once per host instance. |
| 294 if (!sequence_id_was_set_) { | 305 if (!sequence_id_was_set_) { |
| 295 ResendStanza(); | 306 ResendStanza(); |
| 296 } else { | 307 } else { |
| 297 HOST_LOG << "The heartbeat sequence ID has been set more than once: " | 308 HOST_LOG << "The heartbeat sequence ID has been set more than once: " |
| 298 << "the new value is " << sequence_id; | 309 << "the new value is " << sequence_id; |
| 299 double delay = pow(2.0, sequence_id_recent_set_num_) * | 310 base::TimeDelta delay = pow(2.0, sequence_id_recent_set_num_) * |
| 300 (1 + base::RandDouble()) * kResendDelayMs; | 311 (1 + base::RandDouble()) * kResendDelay; |
| 301 if (delay <= interval_ms_) { | 312 if (delay <= interval_) { |
| 302 timer_resend_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(delay), | 313 timer_resend_.Start(FROM_HERE, delay, this, |
| 303 this, &HeartbeatSender::ResendStanza); | 314 &HeartbeatSender::ResendStanza); |
| 304 } | 315 } |
| 305 } | 316 } |
| 306 sequence_id_was_set_ = true; | 317 sequence_id_was_set_ = true; |
| 307 } | 318 } |
| 308 | 319 |
| 309 std::unique_ptr<XmlElement> HeartbeatSender::CreateHeartbeatMessage() { | 320 std::unique_ptr<XmlElement> HeartbeatSender::CreateHeartbeatMessage() { |
| 310 // Create heartbeat stanza. | 321 // Create heartbeat stanza. |
| 311 std::unique_ptr<XmlElement> heartbeat( | 322 std::unique_ptr<XmlElement> heartbeat( |
| 312 new XmlElement(QName(kChromotingXmlNamespace, kHeartbeatQueryTag))); | 323 new XmlElement(QName(kChromotingXmlNamespace, kHeartbeatQueryTag))); |
| 313 heartbeat->AddAttr(QName(kChromotingXmlNamespace, kHostIdAttr), host_id_); | 324 heartbeat->AddAttr(QName(kChromotingXmlNamespace, kHostIdAttr), host_id_); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 352 | 363 |
| 353 std::string message = signal_strategy_->GetLocalAddress().jid() + ' ' + | 364 std::string message = signal_strategy_->GetLocalAddress().jid() + ' ' + |
| 354 base::IntToString(sequence_id_); | 365 base::IntToString(sequence_id_); |
| 355 std::string signature(host_key_pair_->SignMessage(message)); | 366 std::string signature(host_key_pair_->SignMessage(message)); |
| 356 signature_tag->AddText(signature); | 367 signature_tag->AddText(signature); |
| 357 | 368 |
| 358 return signature_tag; | 369 return signature_tag; |
| 359 } | 370 } |
| 360 | 371 |
| 361 } // namespace remoting | 372 } // namespace remoting |
| OLD | NEW |