blob: ceb45ed278f325e5f325c712625629e3c962eaf3 [file] [log] [blame]
Vitaly Buka4615e0d2015-10-14 15:35:12 -07001// Copyright 2015 The Weave Authors. All rights reserved.
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Stefan Sauer2d16dfa2015-09-25 17:08:35 +02005#include "src/notification/xmpp_channel.h"
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -07006
7#include <string>
8
9#include <base/bind.h>
Vitaly Buka1e363672015-09-25 14:01:16 -070010#include <weave/provider/network.h>
11#include <weave/provider/task_runner.h>
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070012
Stefan Sauer2d16dfa2015-09-25 17:08:35 +020013#include "src/backoff_entry.h"
14#include "src/data_encoding.h"
15#include "src/notification/notification_delegate.h"
16#include "src/notification/notification_parser.h"
17#include "src/notification/xml_node.h"
18#include "src/privet/openssl_utils.h"
19#include "src/utils.h"
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070020
Vitaly Bukab6f015a2015-07-09 14:59:23 -070021namespace weave {
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070022
23namespace {
24
25std::string BuildXmppStartStreamCommand() {
26 return "<stream:stream to='clouddevices.gserviceaccount.com' "
Vitaly Bukaa647c852015-07-06 14:51:01 -070027 "xmlns:stream='http://etherx.jabber.org/streams' "
28 "xml:lang='*' version='1.0' xmlns='jabber:client'>";
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070029}
30
Vitaly Bukaa647c852015-07-06 14:51:01 -070031std::string BuildXmppAuthenticateCommand(const std::string& account,
32 const std::string& token) {
Vitaly Bukaa04405e2015-08-13 18:28:14 -070033 std::vector<uint8_t> credentials;
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070034 credentials.push_back(0);
35 credentials.insert(credentials.end(), account.begin(), account.end());
36 credentials.push_back(0);
37 credentials.insert(credentials.end(), token.begin(), token.end());
Vitaly Bukaa647c852015-07-06 14:51:01 -070038 std::string msg =
39 "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070040 "mechanism='X-OAUTH2' auth:service='oauth2' "
41 "auth:allow-non-google-login='true' "
42 "auth:client-uses-full-bind-result='true' "
43 "xmlns:auth='http://www.google.com/talk/protocol/auth'>" +
Vitaly Buka7d556392015-08-13 20:06:48 -070044 Base64Encode(credentials) + "</auth>";
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070045 return msg;
46}
47
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070048// Backoff policy.
49// Note: In order to ensure a minimum of 20 seconds between server errors,
50// we have a 30s +- 10s (33%) jitter initial backoff.
Vitaly Buka0f80f7c2015-08-13 00:57:25 -070051const BackoffEntry::Policy kDefaultBackoffPolicy = {
Vitaly Bukaa647c852015-07-06 14:51:01 -070052 // Number of initial errors (in sequence) to ignore before applying
53 // exponential back-off rules.
54 0,
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070055
Vitaly Bukaa647c852015-07-06 14:51:01 -070056 // Initial delay for exponential back-off in ms.
57 30 * 1000, // 30 seconds.
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070058
Vitaly Bukaa647c852015-07-06 14:51:01 -070059 // Factor by which the waiting time will be multiplied.
60 2,
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070061
Vitaly Bukaa647c852015-07-06 14:51:01 -070062 // Fuzzing percentage. ex: 10% will spread requests randomly
63 // between 90%-100% of the calculated time.
64 0.33, // 33%.
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070065
Vitaly Bukaa647c852015-07-06 14:51:01 -070066 // Maximum amount of time we are willing to delay our request in ms.
67 10 * 60 * 1000, // 10 minutes.
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070068
Vitaly Bukaa647c852015-07-06 14:51:01 -070069 // Time to keep an entry from being discarded even when it
70 // has no significant state, -1 to never discard.
71 -1,
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070072
Vitaly Bukaa647c852015-07-06 14:51:01 -070073 // Don't use initial delay unless the last request was an error.
74 false,
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070075};
76
77const char kDefaultXmppHost[] = "talk.google.com";
Vitaly Bukaa4b39832015-09-09 02:11:03 -070078const uint16_t kDefaultXmppPort = 5223;
Vitaly Buka63cc3d22015-06-23 20:11:36 -070079
80// Used for keeping connection alive.
81const int kRegularPingIntervalSeconds = 60;
82const int kRegularPingTimeoutSeconds = 30;
83
84// Used for diagnostic when connectivity changed.
85const int kAgressivePingIntervalSeconds = 5;
86const int kAgressivePingTimeoutSeconds = 10;
87
88const int kConnectingTimeoutAfterNetChangeSeconds = 30;
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070089
90} // namespace
91
Vitaly Buka10c69ec2015-08-12 16:17:16 -070092XmppChannel::XmppChannel(const std::string& account,
93 const std::string& access_token,
Vitaly Buka1e363672015-09-25 14:01:16 -070094 provider::TaskRunner* task_runner,
95 provider::Network* network)
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070096 : account_{account},
97 access_token_{access_token},
Vitaly Buka10c69ec2015-08-12 16:17:16 -070098 network_{network},
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070099 backoff_entry_{&kDefaultBackoffPolicy},
Vitaly Bukaf9630fb2015-08-12 21:15:40 -0700100 task_runner_{task_runner},
101 iq_stanza_handler_{new IqStanzaHandler{this, task_runner}} {
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700102 read_socket_data_.resize(4096);
Vitaly Buka387b4f42015-07-30 23:55:13 -0700103 if (network) {
Vitaly Buka3ab6f6e2015-09-24 13:16:16 -0700104 network->AddConnectionChangedCallback(base::Bind(
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700105 &XmppChannel::OnConnectivityChanged, weak_ptr_factory_.GetWeakPtr()));
106 }
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700107}
108
Vitaly Buka74763422015-10-11 00:39:52 -0700109void XmppChannel::OnMessageRead(size_t size, ErrorPtr error) {
110 read_pending_ = false;
111 if (error)
112 return Restart();
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700113 std::string msg(read_socket_data_.data(), size);
Vitaly Buka60d45092015-09-14 11:28:58 -0700114 VLOG(2) << "Received XMPP packet: '" << msg << "'";
Vitaly Buka60d45092015-09-14 11:28:58 -0700115
116 if (!size)
117 return Restart();
118
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700119 stream_parser_.ParseData(msg);
120 WaitForMessage();
121}
122
123void XmppChannel::OnStreamStart(const std::string& node_name,
124 std::map<std::string, std::string> attributes) {
125 VLOG(2) << "XMPP stream start: " << node_name;
126}
127
128void XmppChannel::OnStreamEnd(const std::string& node_name) {
Alex Vakulenko6b028ae2015-05-29 09:38:59 -0700129 VLOG(2) << "XMPP stream ended: " << node_name;
Alex Vakulenko94fe16c2015-06-23 12:30:11 -0700130 Stop();
Alex Vakulenko6b028ae2015-05-29 09:38:59 -0700131 if (IsConnected()) {
132 // If we had a fully-established connection, restart it now.
133 // However, if the connection has never been established yet (e.g.
134 // authorization failed), do not restart right now. Wait till we get
135 // new credentials.
Vitaly Buka823fdda2015-08-13 00:33:00 -0700136 task_runner_->PostDelayedTask(
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700137 FROM_HERE,
Vitaly Buka823fdda2015-08-13 00:33:00 -0700138 base::Bind(&XmppChannel::Restart, task_ptr_factory_.GetWeakPtr()), {});
Alex Vakulenko6b028ae2015-05-29 09:38:59 -0700139 } else if (delegate_) {
140 delegate_->OnPermanentFailure();
141 }
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700142}
143
144void XmppChannel::OnStanza(std::unique_ptr<XmlNode> stanza) {
145 // Handle stanza asynchronously, since XmppChannel::OnStanza() is a callback
146 // from expat XML parser and some stanza could cause the XMPP stream to be
147 // reset and the parser to be re-initialized. We don't want to destroy the
148 // parser while it is performing a callback invocation.
Vitaly Buka823fdda2015-08-13 00:33:00 -0700149 task_runner_->PostDelayedTask(
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700150 FROM_HERE,
151 base::Bind(&XmppChannel::HandleStanza, task_ptr_factory_.GetWeakPtr(),
Vitaly Buka823fdda2015-08-13 00:33:00 -0700152 base::Passed(std::move(stanza))),
153 {});
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700154}
155
156void XmppChannel::HandleStanza(std::unique_ptr<XmlNode> stanza) {
157 VLOG(2) << "XMPP stanza received: " << stanza->ToString();
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700158
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700159 switch (state_) {
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700160 case XmppState::kConnected:
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700161 if (stanza->name() == "stream:features") {
162 auto children = stanza->FindChildren("mechanisms/mechanism", false);
163 for (const auto& child : children) {
Alex Vakulenko0444c602015-05-22 10:57:09 -0700164 if (child->text() == "X-OAUTH2") {
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700165 state_ = XmppState::kAuthenticationStarted;
166 SendMessage(BuildXmppAuthenticateCommand(account_, access_token_));
167 return;
168 }
169 }
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700170 }
171 break;
172 case XmppState::kAuthenticationStarted:
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700173 if (stanza->name() == "success") {
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700174 state_ = XmppState::kStreamRestartedPostAuthentication;
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700175 RestartXmppStream();
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700176 return;
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700177 } else if (stanza->name() == "failure") {
178 if (stanza->FindFirstChild("not-authorized", false)) {
179 state_ = XmppState::kAuthenticationFailed;
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700180 return;
181 }
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700182 }
183 break;
184 case XmppState::kStreamRestartedPostAuthentication:
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700185 if (stanza->name() == "stream:features" &&
186 stanza->FindFirstChild("bind", false)) {
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700187 state_ = XmppState::kBindSent;
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700188 iq_stanza_handler_->SendRequest(
189 "set", "", "", "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>",
190 base::Bind(&XmppChannel::OnBindCompleted,
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700191 task_ptr_factory_.GetWeakPtr()),
192 base::Bind(&XmppChannel::Restart, task_ptr_factory_.GetWeakPtr()));
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700193 return;
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700194 }
195 break;
196 default:
Alex Vakulenko6e3c30e2015-05-21 17:39:25 -0700197 if (stanza->name() == "message") {
198 HandleMessageStanza(std::move(stanza));
199 return;
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700200 } else if (stanza->name() == "iq") {
201 if (!iq_stanza_handler_->HandleIqStanza(std::move(stanza))) {
202 LOG(ERROR) << "Failed to handle IQ stanza";
203 CloseStream();
204 }
205 return;
Alex Vakulenko6e3c30e2015-05-21 17:39:25 -0700206 }
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700207 LOG(INFO) << "Unexpected XMPP stanza ignored: " << stanza->ToString();
208 return;
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700209 }
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700210 // Something bad happened. Close the stream and start over.
211 LOG(ERROR) << "Error condition occurred handling stanza: "
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700212 << stanza->ToString() << " in state: " << static_cast<int>(state_);
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700213 CloseStream();
214}
215
216void XmppChannel::CloseStream() {
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700217 SendMessage("</stream:stream>");
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700218}
219
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700220void XmppChannel::OnBindCompleted(std::unique_ptr<XmlNode> reply) {
221 if (reply->GetAttributeOrEmpty("type") != "result") {
222 CloseStream();
223 return;
224 }
225 const XmlNode* jid_node = reply->FindFirstChild("bind/jid", false);
226 if (!jid_node) {
227 LOG(ERROR) << "XMPP Bind response is missing JID";
228 CloseStream();
229 return;
230 }
231
232 jid_ = jid_node->text();
233 state_ = XmppState::kSessionStarted;
234 iq_stanza_handler_->SendRequest(
235 "set", "", "", "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>",
236 base::Bind(&XmppChannel::OnSessionEstablished,
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700237 task_ptr_factory_.GetWeakPtr()),
238 base::Bind(&XmppChannel::Restart, task_ptr_factory_.GetWeakPtr()));
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700239}
240
241void XmppChannel::OnSessionEstablished(std::unique_ptr<XmlNode> reply) {
242 if (reply->GetAttributeOrEmpty("type") != "result") {
243 CloseStream();
244 return;
245 }
246 state_ = XmppState::kSubscribeStarted;
Vitaly Bukaa647c852015-07-06 14:51:01 -0700247 std::string body =
248 "<subscribe xmlns='google:push'>"
249 "<item channel='cloud_devices' from=''/></subscribe>";
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700250 iq_stanza_handler_->SendRequest(
251 "set", "", account_, body,
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700252 base::Bind(&XmppChannel::OnSubscribed, task_ptr_factory_.GetWeakPtr()),
253 base::Bind(&XmppChannel::Restart, task_ptr_factory_.GetWeakPtr()));
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700254}
255
256void XmppChannel::OnSubscribed(std::unique_ptr<XmlNode> reply) {
257 if (reply->GetAttributeOrEmpty("type") != "result") {
258 CloseStream();
259 return;
260 }
261 state_ = XmppState::kSubscribed;
262 if (delegate_)
263 delegate_->OnConnected(GetName());
264}
265
Alex Vakulenko6e3c30e2015-05-21 17:39:25 -0700266void XmppChannel::HandleMessageStanza(std::unique_ptr<XmlNode> stanza) {
267 const XmlNode* node = stanza->FindFirstChild("push:push/push:data", true);
268 if (!node) {
269 LOG(WARNING) << "XMPP message stanza is missing <push:data> element";
270 return;
271 }
272 std::string data = node->text();
273 std::string json_data;
Vitaly Buka7d556392015-08-13 20:06:48 -0700274 if (!Base64Decode(data, &json_data)) {
Alex Vakulenko6e3c30e2015-05-21 17:39:25 -0700275 LOG(WARNING) << "Failed to decode base64-encoded message payload: " << data;
276 return;
277 }
278
279 VLOG(2) << "XMPP push notification data: " << json_data;
280 auto json_dict = LoadJsonDict(json_data, nullptr);
281 if (json_dict && delegate_)
Alex Vakulenkoe07c29d2015-10-22 10:31:12 -0700282 ParseNotificationJson(*json_dict, delegate_, GetName());
Alex Vakulenko6e3c30e2015-05-21 17:39:25 -0700283}
284
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700285void XmppChannel::CreateSslSocket() {
286 CHECK(!stream_);
287 state_ = XmppState::kConnecting;
Vitaly Buka4ebd3292015-09-23 18:04:17 -0700288 LOG(INFO) << "Starting XMPP connection to " << kDefaultXmppHost << ":"
289 << kDefaultXmppPort;
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700290
Vitaly Buka74763422015-10-11 00:39:52 -0700291 network_->OpenSslSocket(kDefaultXmppHost, kDefaultXmppPort,
292 base::Bind(&XmppChannel::OnSslSocketReady,
293 task_ptr_factory_.GetWeakPtr()));
Alex Vakulenko2684b512015-05-19 13:42:10 -0700294}
295
Vitaly Buka74763422015-10-11 00:39:52 -0700296void XmppChannel::OnSslSocketReady(std::unique_ptr<Stream> stream,
297 ErrorPtr error) {
298 if (error) {
299 LOG(ERROR) << "TLS handshake failed. Restarting XMPP connection";
300 backoff_entry_.InformOfRequest(false);
301
302 LOG(INFO) << "Delaying connection to XMPP server for "
303 << backoff_entry_.GetTimeUntilRelease();
304 return task_runner_->PostDelayedTask(
305 FROM_HERE, base::Bind(&XmppChannel::CreateSslSocket,
306 task_ptr_factory_.GetWeakPtr()),
307 backoff_entry_.GetTimeUntilRelease());
308 }
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700309 CHECK(XmppState::kConnecting == state_);
310 backoff_entry_.InformOfRequest(true);
311 stream_ = std::move(stream);
312 state_ = XmppState::kConnected;
Alex Vakulenko2684b512015-05-19 13:42:10 -0700313 RestartXmppStream();
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700314 ScheduleRegularPing();
Alex Vakulenko2684b512015-05-19 13:42:10 -0700315}
316
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700317void XmppChannel::SendMessage(const std::string& message) {
Alex Vakulenkoe63dde62015-07-08 10:58:01 -0700318 CHECK(stream_) << "No XMPP socket stream available";
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700319 if (write_pending_) {
320 queued_write_data_ += message;
321 return;
322 }
323 write_socket_data_ = queued_write_data_ + message;
324 queued_write_data_.clear();
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700325 VLOG(2) << "Sending XMPP message: " << message;
326
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700327 write_pending_ = true;
Vitaly Bukada987282015-09-23 16:50:40 -0700328 stream_->Write(
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700329 write_socket_data_.data(), write_socket_data_.size(),
Vitaly Buka74763422015-10-11 00:39:52 -0700330 base::Bind(&XmppChannel::OnMessageSent, task_ptr_factory_.GetWeakPtr()));
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700331}
332
Vitaly Buka74763422015-10-11 00:39:52 -0700333void XmppChannel::OnMessageSent(ErrorPtr error) {
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700334 write_pending_ = false;
Vitaly Buka74763422015-10-11 00:39:52 -0700335 if (error)
336 return Restart();
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700337 if (queued_write_data_.empty()) {
338 WaitForMessage();
339 } else {
340 SendMessage(std::string{});
341 }
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700342}
343
344void XmppChannel::WaitForMessage() {
Alex Vakulenko94fe16c2015-06-23 12:30:11 -0700345 if (read_pending_ || !stream_)
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700346 return;
347
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700348 read_pending_ = true;
Vitaly Bukada987282015-09-23 16:50:40 -0700349 stream_->Read(
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700350 read_socket_data_.data(), read_socket_data_.size(),
Vitaly Buka74763422015-10-11 00:39:52 -0700351 base::Bind(&XmppChannel::OnMessageRead, task_ptr_factory_.GetWeakPtr()));
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700352}
353
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700354std::string XmppChannel::GetName() const {
355 return "xmpp";
356}
357
Alex Vakulenko6b028ae2015-05-29 09:38:59 -0700358bool XmppChannel::IsConnected() const {
359 return state_ == XmppState::kSubscribed;
360}
361
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700362void XmppChannel::AddChannelParameters(base::DictionaryValue* channel_json) {
363 // No extra parameters needed for XMPP.
364}
365
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700366void XmppChannel::Restart() {
Alex Vakulenkobfdd3102015-09-08 15:10:54 -0700367 LOG(INFO) << "Restarting XMPP";
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700368 Stop();
369 Start(delegate_);
370}
371
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700372void XmppChannel::Start(NotificationDelegate* delegate) {
373 CHECK(state_ == XmppState::kNotStarted);
374 delegate_ = delegate;
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700375
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700376 CreateSslSocket();
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700377}
378
379void XmppChannel::Stop() {
Alex Vakulenko6b028ae2015-05-29 09:38:59 -0700380 if (IsConnected() && delegate_)
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700381 delegate_->OnDisconnected();
382
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700383 task_ptr_factory_.InvalidateWeakPtrs();
384 ping_ptr_factory_.InvalidateWeakPtrs();
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700385
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700386 stream_.reset();
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700387 state_ = XmppState::kNotStarted;
388}
389
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700390void XmppChannel::RestartXmppStream() {
391 stream_parser_.Reset();
Vitaly Bukada987282015-09-23 16:50:40 -0700392 stream_->CancelPendingOperations();
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700393 read_pending_ = false;
394 write_pending_ = false;
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700395 SendMessage(BuildXmppStartStreamCommand());
396}
397
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700398void XmppChannel::SchedulePing(base::TimeDelta interval,
399 base::TimeDelta timeout) {
400 VLOG(1) << "Next XMPP ping in " << interval << " with timeout " << timeout;
401 ping_ptr_factory_.InvalidateWeakPtrs();
Vitaly Bukaf9630fb2015-08-12 21:15:40 -0700402 task_runner_->PostDelayedTask(
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700403 FROM_HERE, base::Bind(&XmppChannel::PingServer,
404 ping_ptr_factory_.GetWeakPtr(), timeout),
405 interval);
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700406}
407
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700408void XmppChannel::ScheduleRegularPing() {
409 SchedulePing(base::TimeDelta::FromSeconds(kRegularPingIntervalSeconds),
410 base::TimeDelta::FromSeconds(kRegularPingTimeoutSeconds));
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700411}
412
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700413void XmppChannel::ScheduleFastPing() {
414 SchedulePing(base::TimeDelta::FromSeconds(kAgressivePingIntervalSeconds),
415 base::TimeDelta::FromSeconds(kAgressivePingTimeoutSeconds));
416}
417
418void XmppChannel::PingServer(base::TimeDelta timeout) {
419 VLOG(1) << "Sending XMPP ping";
Alex Vakulenkoe63dde62015-07-08 10:58:01 -0700420 if (!IsConnected()) {
421 LOG(WARNING) << "XMPP channel is not connected";
422 Restart();
423 return;
424 }
425
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700426 // Send an XMPP Ping request as defined in XEP-0199 extension:
427 // http://xmpp.org/extensions/xep-0199.html
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700428 iq_stanza_handler_->SendRequestWithCustomTimeout(
429 "get", jid_, account_, "<ping xmlns='urn:xmpp:ping'/>", timeout,
430 base::Bind(&XmppChannel::OnPingResponse, task_ptr_factory_.GetWeakPtr(),
431 base::Time::Now()),
432 base::Bind(&XmppChannel::OnPingTimeout, task_ptr_factory_.GetWeakPtr(),
433 base::Time::Now()));
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700434}
435
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700436void XmppChannel::OnPingResponse(base::Time sent_time,
437 std::unique_ptr<XmlNode> reply) {
438 VLOG(1) << "XMPP response received after " << (base::Time::Now() - sent_time);
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700439 // Ping response received from server. Everything seems to be in order.
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700440 // Reschedule with default intervals.
441 ScheduleRegularPing();
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700442}
443
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700444void XmppChannel::OnPingTimeout(base::Time sent_time) {
445 LOG(WARNING) << "XMPP channel seems to be disconnected. Ping timed out after "
446 << (base::Time::Now() - sent_time);
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700447 Restart();
448}
449
Vitaly Bukabf6840a2015-09-21 13:38:56 -0700450void XmppChannel::OnConnectivityChanged() {
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700451 if (state_ == XmppState::kNotStarted)
452 return;
453
454 if (state_ == XmppState::kConnecting &&
455 backoff_entry_.GetTimeUntilRelease() <
456 base::TimeDelta::FromSeconds(
457 kConnectingTimeoutAfterNetChangeSeconds)) {
458 VLOG(1) << "Next reconnect in " << backoff_entry_.GetTimeUntilRelease();
459 return;
460 }
461
462 ScheduleFastPing();
463}
464
Vitaly Bukab6f015a2015-07-09 14:59:23 -0700465} // namespace weave