blob: 4734549460a1209cd369a5e632ef85e71610a137 [file] [log] [blame]
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -07001// Copyright 2015 The Chromium OS 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.
4
Vitaly Buka912b6982015-07-06 11:13:03 -07005#include "libweave/src/notification/xmpp_channel.h"
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -07006
7#include <string>
8
9#include <base/bind.h>
Vitaly Buka3ab6f6e2015-09-24 13:16:16 -070010#include <weave/network_provider.h>
Vitaly Bukaf9630fb2015-08-12 21:15:40 -070011#include <weave/task_runner.h>
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070012
Vitaly Buka0f80f7c2015-08-13 00:57:25 -070013#include "libweave/src/backoff_entry.h"
Vitaly Buka7d556392015-08-13 20:06:48 -070014#include "libweave/src/data_encoding.h"
Vitaly Buka912b6982015-07-06 11:13:03 -070015#include "libweave/src/notification/notification_delegate.h"
16#include "libweave/src/notification/notification_parser.h"
17#include "libweave/src/notification/xml_node.h"
Vitaly Bukaa04405e2015-08-13 18:28:14 -070018#include "libweave/src/privet/openssl_utils.h"
Vitaly Buka912b6982015-07-06 11:13:03 -070019#include "libweave/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 Bukaf9630fb2015-08-12 21:15:40 -070094 TaskRunner* task_runner,
Vitaly Buka3ab6f6e2015-09-24 13:16:16 -070095 NetworkProvider* 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
109void XmppChannel::OnMessageRead(size_t size) {
110 std::string msg(read_socket_data_.data(), size);
Vitaly Buka60d45092015-09-14 11:28:58 -0700111 VLOG(2) << "Received XMPP packet: '" << msg << "'";
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700112 read_pending_ = false;
Vitaly Buka60d45092015-09-14 11:28:58 -0700113
114 if (!size)
115 return Restart();
116
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700117 stream_parser_.ParseData(msg);
118 WaitForMessage();
119}
120
121void XmppChannel::OnStreamStart(const std::string& node_name,
122 std::map<std::string, std::string> attributes) {
123 VLOG(2) << "XMPP stream start: " << node_name;
124}
125
126void XmppChannel::OnStreamEnd(const std::string& node_name) {
Alex Vakulenko6b028ae2015-05-29 09:38:59 -0700127 VLOG(2) << "XMPP stream ended: " << node_name;
Alex Vakulenko94fe16c2015-06-23 12:30:11 -0700128 Stop();
Alex Vakulenko6b028ae2015-05-29 09:38:59 -0700129 if (IsConnected()) {
130 // If we had a fully-established connection, restart it now.
131 // However, if the connection has never been established yet (e.g.
132 // authorization failed), do not restart right now. Wait till we get
133 // new credentials.
Vitaly Buka823fdda2015-08-13 00:33:00 -0700134 task_runner_->PostDelayedTask(
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700135 FROM_HERE,
Vitaly Buka823fdda2015-08-13 00:33:00 -0700136 base::Bind(&XmppChannel::Restart, task_ptr_factory_.GetWeakPtr()), {});
Alex Vakulenko6b028ae2015-05-29 09:38:59 -0700137 } else if (delegate_) {
138 delegate_->OnPermanentFailure();
139 }
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700140}
141
142void XmppChannel::OnStanza(std::unique_ptr<XmlNode> stanza) {
143 // Handle stanza asynchronously, since XmppChannel::OnStanza() is a callback
144 // from expat XML parser and some stanza could cause the XMPP stream to be
145 // reset and the parser to be re-initialized. We don't want to destroy the
146 // parser while it is performing a callback invocation.
Vitaly Buka823fdda2015-08-13 00:33:00 -0700147 task_runner_->PostDelayedTask(
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700148 FROM_HERE,
149 base::Bind(&XmppChannel::HandleStanza, task_ptr_factory_.GetWeakPtr(),
Vitaly Buka823fdda2015-08-13 00:33:00 -0700150 base::Passed(std::move(stanza))),
151 {});
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700152}
153
154void XmppChannel::HandleStanza(std::unique_ptr<XmlNode> stanza) {
155 VLOG(2) << "XMPP stanza received: " << stanza->ToString();
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700156
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700157 switch (state_) {
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700158 case XmppState::kConnected:
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700159 if (stanza->name() == "stream:features") {
160 auto children = stanza->FindChildren("mechanisms/mechanism", false);
161 for (const auto& child : children) {
Alex Vakulenko0444c602015-05-22 10:57:09 -0700162 if (child->text() == "X-OAUTH2") {
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700163 state_ = XmppState::kAuthenticationStarted;
164 SendMessage(BuildXmppAuthenticateCommand(account_, access_token_));
165 return;
166 }
167 }
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700168 }
169 break;
170 case XmppState::kAuthenticationStarted:
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700171 if (stanza->name() == "success") {
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700172 state_ = XmppState::kStreamRestartedPostAuthentication;
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700173 RestartXmppStream();
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700174 return;
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700175 } else if (stanza->name() == "failure") {
176 if (stanza->FindFirstChild("not-authorized", false)) {
177 state_ = XmppState::kAuthenticationFailed;
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700178 return;
179 }
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700180 }
181 break;
182 case XmppState::kStreamRestartedPostAuthentication:
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700183 if (stanza->name() == "stream:features" &&
184 stanza->FindFirstChild("bind", false)) {
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700185 state_ = XmppState::kBindSent;
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700186 iq_stanza_handler_->SendRequest(
187 "set", "", "", "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>",
188 base::Bind(&XmppChannel::OnBindCompleted,
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700189 task_ptr_factory_.GetWeakPtr()),
190 base::Bind(&XmppChannel::Restart, task_ptr_factory_.GetWeakPtr()));
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700191 return;
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700192 }
193 break;
194 default:
Alex Vakulenko6e3c30e2015-05-21 17:39:25 -0700195 if (stanza->name() == "message") {
196 HandleMessageStanza(std::move(stanza));
197 return;
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700198 } else if (stanza->name() == "iq") {
199 if (!iq_stanza_handler_->HandleIqStanza(std::move(stanza))) {
200 LOG(ERROR) << "Failed to handle IQ stanza";
201 CloseStream();
202 }
203 return;
Alex Vakulenko6e3c30e2015-05-21 17:39:25 -0700204 }
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700205 LOG(INFO) << "Unexpected XMPP stanza ignored: " << stanza->ToString();
206 return;
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700207 }
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700208 // Something bad happened. Close the stream and start over.
209 LOG(ERROR) << "Error condition occurred handling stanza: "
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700210 << stanza->ToString() << " in state: " << static_cast<int>(state_);
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700211 CloseStream();
212}
213
214void XmppChannel::CloseStream() {
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700215 SendMessage("</stream:stream>");
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700216}
217
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700218void XmppChannel::OnBindCompleted(std::unique_ptr<XmlNode> reply) {
219 if (reply->GetAttributeOrEmpty("type") != "result") {
220 CloseStream();
221 return;
222 }
223 const XmlNode* jid_node = reply->FindFirstChild("bind/jid", false);
224 if (!jid_node) {
225 LOG(ERROR) << "XMPP Bind response is missing JID";
226 CloseStream();
227 return;
228 }
229
230 jid_ = jid_node->text();
231 state_ = XmppState::kSessionStarted;
232 iq_stanza_handler_->SendRequest(
233 "set", "", "", "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>",
234 base::Bind(&XmppChannel::OnSessionEstablished,
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700235 task_ptr_factory_.GetWeakPtr()),
236 base::Bind(&XmppChannel::Restart, task_ptr_factory_.GetWeakPtr()));
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700237}
238
239void XmppChannel::OnSessionEstablished(std::unique_ptr<XmlNode> reply) {
240 if (reply->GetAttributeOrEmpty("type") != "result") {
241 CloseStream();
242 return;
243 }
244 state_ = XmppState::kSubscribeStarted;
Vitaly Bukaa647c852015-07-06 14:51:01 -0700245 std::string body =
246 "<subscribe xmlns='google:push'>"
247 "<item channel='cloud_devices' from=''/></subscribe>";
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700248 iq_stanza_handler_->SendRequest(
249 "set", "", account_, body,
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700250 base::Bind(&XmppChannel::OnSubscribed, task_ptr_factory_.GetWeakPtr()),
251 base::Bind(&XmppChannel::Restart, task_ptr_factory_.GetWeakPtr()));
Alex Vakulenkodea76b22015-06-01 13:18:06 -0700252}
253
254void XmppChannel::OnSubscribed(std::unique_ptr<XmlNode> reply) {
255 if (reply->GetAttributeOrEmpty("type") != "result") {
256 CloseStream();
257 return;
258 }
259 state_ = XmppState::kSubscribed;
260 if (delegate_)
261 delegate_->OnConnected(GetName());
262}
263
Alex Vakulenko6e3c30e2015-05-21 17:39:25 -0700264void XmppChannel::HandleMessageStanza(std::unique_ptr<XmlNode> stanza) {
265 const XmlNode* node = stanza->FindFirstChild("push:push/push:data", true);
266 if (!node) {
267 LOG(WARNING) << "XMPP message stanza is missing <push:data> element";
268 return;
269 }
270 std::string data = node->text();
271 std::string json_data;
Vitaly Buka7d556392015-08-13 20:06:48 -0700272 if (!Base64Decode(data, &json_data)) {
Alex Vakulenko6e3c30e2015-05-21 17:39:25 -0700273 LOG(WARNING) << "Failed to decode base64-encoded message payload: " << data;
274 return;
275 }
276
277 VLOG(2) << "XMPP push notification data: " << json_data;
278 auto json_dict = LoadJsonDict(json_data, nullptr);
279 if (json_dict && delegate_)
280 ParseNotificationJson(*json_dict, delegate_);
281}
282
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700283void XmppChannel::CreateSslSocket() {
284 CHECK(!stream_);
285 state_ = XmppState::kConnecting;
Vitaly Buka4ebd3292015-09-23 18:04:17 -0700286 LOG(INFO) << "Starting XMPP connection to " << kDefaultXmppHost << ":"
287 << kDefaultXmppPort;
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700288
289 network_->OpenSslSocket(
290 kDefaultXmppHost, kDefaultXmppPort,
291 base::Bind(&XmppChannel::OnSslSocketReady,
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700292 task_ptr_factory_.GetWeakPtr()),
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700293 base::Bind(&XmppChannel::OnSslError, task_ptr_factory_.GetWeakPtr()));
Alex Vakulenko2684b512015-05-19 13:42:10 -0700294}
295
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700296void XmppChannel::OnSslSocketReady(std::unique_ptr<Stream> stream) {
297 CHECK(XmppState::kConnecting == state_);
298 backoff_entry_.InformOfRequest(true);
299 stream_ = std::move(stream);
300 state_ = XmppState::kConnected;
Alex Vakulenko2684b512015-05-19 13:42:10 -0700301 RestartXmppStream();
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700302 ScheduleRegularPing();
Alex Vakulenko2684b512015-05-19 13:42:10 -0700303}
304
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700305void XmppChannel::OnSslError(const Error* error) {
Alex Vakulenko2684b512015-05-19 13:42:10 -0700306 LOG(ERROR) << "TLS handshake failed. Restarting XMPP connection";
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700307 backoff_entry_.InformOfRequest(false);
308
309 LOG(INFO) << "Delaying connection to XMPP server for "
310 << backoff_entry_.GetTimeUntilRelease();
311 task_runner_->PostDelayedTask(
312 FROM_HERE,
313 base::Bind(&XmppChannel::CreateSslSocket, task_ptr_factory_.GetWeakPtr()),
314 backoff_entry_.GetTimeUntilRelease());
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(),
330 base::Bind(&XmppChannel::OnMessageSent, task_ptr_factory_.GetWeakPtr()),
Vitaly Buka0684cfe2015-09-15 21:32:37 -0700331 base::Bind(&XmppChannel::OnWriteError, task_ptr_factory_.GetWeakPtr()));
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700332}
333
334void XmppChannel::OnMessageSent() {
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700335 ErrorPtr error;
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700336 write_pending_ = false;
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(),
351 base::Bind(&XmppChannel::OnMessageRead, task_ptr_factory_.GetWeakPtr()),
Vitaly Buka0684cfe2015-09-15 21:32:37 -0700352 base::Bind(&XmppChannel::OnReadError, task_ptr_factory_.GetWeakPtr()));
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700353}
354
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700355void XmppChannel::OnReadError(const Error* error) {
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700356 read_pending_ = false;
357 Restart();
358}
359
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700360void XmppChannel::OnWriteError(const Error* error) {
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700361 write_pending_ = false;
362 Restart();
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700363}
364
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700365std::string XmppChannel::GetName() const {
366 return "xmpp";
367}
368
Alex Vakulenko6b028ae2015-05-29 09:38:59 -0700369bool XmppChannel::IsConnected() const {
370 return state_ == XmppState::kSubscribed;
371}
372
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700373void XmppChannel::AddChannelParameters(base::DictionaryValue* channel_json) {
374 // No extra parameters needed for XMPP.
375}
376
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700377void XmppChannel::Restart() {
Alex Vakulenkobfdd3102015-09-08 15:10:54 -0700378 LOG(INFO) << "Restarting XMPP";
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700379 Stop();
380 Start(delegate_);
381}
382
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700383void XmppChannel::Start(NotificationDelegate* delegate) {
384 CHECK(state_ == XmppState::kNotStarted);
385 delegate_ = delegate;
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700386
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700387 CreateSslSocket();
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700388}
389
390void XmppChannel::Stop() {
Alex Vakulenko6b028ae2015-05-29 09:38:59 -0700391 if (IsConnected() && delegate_)
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700392 delegate_->OnDisconnected();
393
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700394 task_ptr_factory_.InvalidateWeakPtrs();
395 ping_ptr_factory_.InvalidateWeakPtrs();
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700396
Vitaly Bukaa4b39832015-09-09 02:11:03 -0700397 stream_.reset();
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700398 state_ = XmppState::kNotStarted;
399}
400
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700401void XmppChannel::RestartXmppStream() {
402 stream_parser_.Reset();
Vitaly Bukada987282015-09-23 16:50:40 -0700403 stream_->CancelPendingOperations();
Alex Vakulenkobf71f702015-05-18 14:30:56 -0700404 read_pending_ = false;
405 write_pending_ = false;
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700406 SendMessage(BuildXmppStartStreamCommand());
407}
408
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700409void XmppChannel::SchedulePing(base::TimeDelta interval,
410 base::TimeDelta timeout) {
411 VLOG(1) << "Next XMPP ping in " << interval << " with timeout " << timeout;
412 ping_ptr_factory_.InvalidateWeakPtrs();
Vitaly Bukaf9630fb2015-08-12 21:15:40 -0700413 task_runner_->PostDelayedTask(
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700414 FROM_HERE, base::Bind(&XmppChannel::PingServer,
415 ping_ptr_factory_.GetWeakPtr(), timeout),
416 interval);
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700417}
418
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700419void XmppChannel::ScheduleRegularPing() {
420 SchedulePing(base::TimeDelta::FromSeconds(kRegularPingIntervalSeconds),
421 base::TimeDelta::FromSeconds(kRegularPingTimeoutSeconds));
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700422}
423
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700424void XmppChannel::ScheduleFastPing() {
425 SchedulePing(base::TimeDelta::FromSeconds(kAgressivePingIntervalSeconds),
426 base::TimeDelta::FromSeconds(kAgressivePingTimeoutSeconds));
427}
428
429void XmppChannel::PingServer(base::TimeDelta timeout) {
430 VLOG(1) << "Sending XMPP ping";
Alex Vakulenkoe63dde62015-07-08 10:58:01 -0700431 if (!IsConnected()) {
432 LOG(WARNING) << "XMPP channel is not connected";
433 Restart();
434 return;
435 }
436
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700437 // Send an XMPP Ping request as defined in XEP-0199 extension:
438 // http://xmpp.org/extensions/xep-0199.html
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700439 iq_stanza_handler_->SendRequestWithCustomTimeout(
440 "get", jid_, account_, "<ping xmlns='urn:xmpp:ping'/>", timeout,
441 base::Bind(&XmppChannel::OnPingResponse, task_ptr_factory_.GetWeakPtr(),
442 base::Time::Now()),
443 base::Bind(&XmppChannel::OnPingTimeout, task_ptr_factory_.GetWeakPtr(),
444 base::Time::Now()));
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700445}
446
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700447void XmppChannel::OnPingResponse(base::Time sent_time,
448 std::unique_ptr<XmlNode> reply) {
449 VLOG(1) << "XMPP response received after " << (base::Time::Now() - sent_time);
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700450 // Ping response received from server. Everything seems to be in order.
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700451 // Reschedule with default intervals.
452 ScheduleRegularPing();
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700453}
454
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700455void XmppChannel::OnPingTimeout(base::Time sent_time) {
456 LOG(WARNING) << "XMPP channel seems to be disconnected. Ping timed out after "
457 << (base::Time::Now() - sent_time);
Alex Vakulenko5cd79972015-06-01 13:21:18 -0700458 Restart();
459}
460
Vitaly Bukabf6840a2015-09-21 13:38:56 -0700461void XmppChannel::OnConnectivityChanged() {
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700462 if (state_ == XmppState::kNotStarted)
463 return;
464
465 if (state_ == XmppState::kConnecting &&
466 backoff_entry_.GetTimeUntilRelease() <
467 base::TimeDelta::FromSeconds(
468 kConnectingTimeoutAfterNetChangeSeconds)) {
469 VLOG(1) << "Next reconnect in " << backoff_entry_.GetTimeUntilRelease();
470 return;
471 }
472
473 ScheduleFastPing();
474}
475
Vitaly Bukab6f015a2015-07-09 14:59:23 -0700476} // namespace weave