|
|
@@ -4,6 +4,7 @@
|
|
|
|
|
|
namespace {
|
|
|
static constexpr int MAX_ALLOWED_FAILED_AUTHENTICATION_ATTEMPTS = 3;
|
|
|
+static constexpr int PING_INTERVAL = 30 * 1000;
|
|
|
}
|
|
|
|
|
|
namespace OCC {
|
|
|
@@ -14,6 +15,13 @@ PushNotifications::PushNotifications(Account *account, QObject *parent)
|
|
|
: QObject(parent)
|
|
|
, _account(account)
|
|
|
{
|
|
|
+ connect(&_pingTimer, &QTimer::timeout, this, &PushNotifications::pingWebSocketServer);
|
|
|
+ _pingTimer.setSingleShot(true);
|
|
|
+ _pingTimer.setInterval(PING_INTERVAL);
|
|
|
+
|
|
|
+ connect(&_pingTimedOutTimer, &QTimer::timeout, this, &PushNotifications::onPingTimedOut);
|
|
|
+ _pingTimedOutTimer.setSingleShot(true);
|
|
|
+ _pingTimedOutTimer.setInterval(PING_INTERVAL);
|
|
|
}
|
|
|
|
|
|
PushNotifications::~PushNotifications()
|
|
|
@@ -23,7 +31,7 @@ PushNotifications::~PushNotifications()
|
|
|
|
|
|
void PushNotifications::setup()
|
|
|
{
|
|
|
- _isReady = false;
|
|
|
+ qCInfo(lcPushNotifications) << "Setup push notifications";
|
|
|
_failedAuthenticationAttemptsCount = 0;
|
|
|
reconnectToWebSocket();
|
|
|
}
|
|
|
@@ -36,15 +44,25 @@ void PushNotifications::reconnectToWebSocket()
|
|
|
|
|
|
void PushNotifications::closeWebSocket()
|
|
|
{
|
|
|
+ qCInfo(lcPushNotifications) << "Close websocket" << _webSocket << "for account" << _account->url();
|
|
|
+
|
|
|
+ _pingTimer.stop();
|
|
|
+ _pingTimedOutTimer.stop();
|
|
|
+ _isReady = false;
|
|
|
+
|
|
|
+ // Maybe there run some reconnection attempts
|
|
|
+ if (_reconnectTimer) {
|
|
|
+ _reconnectTimer->stop();
|
|
|
+ }
|
|
|
+
|
|
|
if (_webSocket) {
|
|
|
- qCInfo(lcPushNotifications) << "Close websocket";
|
|
|
_webSocket->close();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void PushNotifications::onWebSocketConnected()
|
|
|
{
|
|
|
- qCInfo(lcPushNotifications) << "Connected to websocket";
|
|
|
+ qCInfo(lcPushNotifications) << "Connected to websocket" << _webSocket << "for account" << _account->url();
|
|
|
|
|
|
connect(_webSocket, &QWebSocket::textMessageReceived, this, &PushNotifications::onWebSocketTextMessageReceived, Qt::UniqueConnection);
|
|
|
|
|
|
@@ -64,7 +82,7 @@ void PushNotifications::authenticateOnWebSocket()
|
|
|
|
|
|
void PushNotifications::onWebSocketDisconnected()
|
|
|
{
|
|
|
- qCInfo(lcPushNotifications) << "Disconnected from websocket";
|
|
|
+ qCInfo(lcPushNotifications) << "Disconnected from websocket" << _webSocket << "for account" << _account->url();
|
|
|
}
|
|
|
|
|
|
void PushNotifications::onWebSocketTextMessageReceived(const QString &message)
|
|
|
@@ -93,8 +111,7 @@ void PushNotifications::onWebSocketError(QAbstractSocket::SocketError error)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- qCWarning(lcPushNotifications) << "Websocket error" << error;
|
|
|
-
|
|
|
+ qCWarning(lcPushNotifications) << "Websocket error on" << _webSocket << "with account" << _account->url() << error;
|
|
|
_isReady = false;
|
|
|
emit connectionLost();
|
|
|
}
|
|
|
@@ -123,7 +140,7 @@ bool PushNotifications::tryReconnectToWebSocket()
|
|
|
|
|
|
void PushNotifications::onWebSocketSslErrors(const QList<QSslError> &errors)
|
|
|
{
|
|
|
- qCWarning(lcPushNotifications) << "Received websocket ssl errors:" << errors;
|
|
|
+ qCWarning(lcPushNotifications) << "Websocket ssl errors on" << _webSocket << "with account" << _account->url() << errors;
|
|
|
_isReady = false;
|
|
|
emit authenticationFailed();
|
|
|
}
|
|
|
@@ -135,8 +152,8 @@ void PushNotifications::openWebSocket()
|
|
|
const auto webSocketUrl = capabilities.pushNotificationsWebSocketUrl();
|
|
|
|
|
|
if (!_webSocket) {
|
|
|
- qCInfo(lcPushNotifications) << "Create websocket";
|
|
|
_webSocket = new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this);
|
|
|
+ qCInfo(lcPushNotifications) << "Created websocket" << _webSocket << "for account" << _account->url();
|
|
|
}
|
|
|
|
|
|
if (_webSocket) {
|
|
|
@@ -144,6 +161,7 @@ void PushNotifications::openWebSocket()
|
|
|
connect(_webSocket, &QWebSocket::sslErrors, this, &PushNotifications::onWebSocketSslErrors, Qt::UniqueConnection);
|
|
|
connect(_webSocket, &QWebSocket::connected, this, &PushNotifications::onWebSocketConnected, Qt::UniqueConnection);
|
|
|
connect(_webSocket, &QWebSocket::disconnected, this, &PushNotifications::onWebSocketDisconnected, Qt::UniqueConnection);
|
|
|
+ connect(_webSocket, &QWebSocket::pong, this, &PushNotifications::onWebSocketPongReceived, Qt::UniqueConnection);
|
|
|
|
|
|
qCInfo(lcPushNotifications) << "Open connection to websocket on:" << webSocketUrl;
|
|
|
_webSocket->open(webSocketUrl);
|
|
|
@@ -165,20 +183,28 @@ void PushNotifications::handleAuthenticated()
|
|
|
qCInfo(lcPushNotifications) << "Authenticated successful on websocket";
|
|
|
_failedAuthenticationAttemptsCount = 0;
|
|
|
_isReady = true;
|
|
|
+ startPingTimer();
|
|
|
emit ready();
|
|
|
+
|
|
|
+ // We maybe reconnected to websocket while being offline for a
|
|
|
+ // while. To not miss any notifications that may have happend,
|
|
|
+ // emit all the signals once.
|
|
|
+ emitFilesChanged();
|
|
|
+ emitNotificationsChanged();
|
|
|
+ emitActivitiesChanged();
|
|
|
}
|
|
|
|
|
|
void PushNotifications::handleNotifyFile()
|
|
|
{
|
|
|
qCInfo(lcPushNotifications) << "Files push notification arrived";
|
|
|
- emit filesChanged(_account);
|
|
|
+ emitFilesChanged();
|
|
|
}
|
|
|
|
|
|
void PushNotifications::handleInvalidCredentials()
|
|
|
{
|
|
|
qCInfo(lcPushNotifications) << "Invalid credentials submitted to websocket";
|
|
|
if (!tryReconnectToWebSocket()) {
|
|
|
- _isReady = false;
|
|
|
+ closeWebSocket();
|
|
|
emit authenticationFailed();
|
|
|
}
|
|
|
}
|
|
|
@@ -186,12 +212,76 @@ void PushNotifications::handleInvalidCredentials()
|
|
|
void PushNotifications::handleNotifyNotification()
|
|
|
{
|
|
|
qCInfo(lcPushNotifications) << "Push notification arrived";
|
|
|
- emit notificationsChanged(_account);
|
|
|
+ emitNotificationsChanged();
|
|
|
}
|
|
|
|
|
|
void PushNotifications::handleNotifyActivity()
|
|
|
{
|
|
|
qCInfo(lcPushNotifications) << "Push activity arrived";
|
|
|
+ emitActivitiesChanged();
|
|
|
+}
|
|
|
+
|
|
|
+void PushNotifications::onWebSocketPongReceived(quint64 /*elapsedTime*/, const QByteArray & /*payload*/)
|
|
|
+{
|
|
|
+ qCDebug(lcPushNotifications) << "Pong received in time";
|
|
|
+ // We are fine with every kind of pong and don't care about the
|
|
|
+ // payload. As long as we receive pongs the server is still alive.
|
|
|
+ _pongReceivedFromWebSocketServer = true;
|
|
|
+ startPingTimer();
|
|
|
+}
|
|
|
+
|
|
|
+void PushNotifications::startPingTimer()
|
|
|
+{
|
|
|
+ _pingTimedOutTimer.stop();
|
|
|
+ _pingTimer.start();
|
|
|
+}
|
|
|
+
|
|
|
+void PushNotifications::startPingTimedOutTimer()
|
|
|
+{
|
|
|
+ _pingTimedOutTimer.start();
|
|
|
+}
|
|
|
+
|
|
|
+void PushNotifications::pingWebSocketServer()
|
|
|
+{
|
|
|
+ Q_ASSERT(_webSocket);
|
|
|
+ qCDebug(lcPushNotifications, "Ping websocket server");
|
|
|
+
|
|
|
+ _pongReceivedFromWebSocketServer = false;
|
|
|
+
|
|
|
+ _webSocket->ping({});
|
|
|
+ startPingTimedOutTimer();
|
|
|
+}
|
|
|
+
|
|
|
+void PushNotifications::onPingTimedOut()
|
|
|
+{
|
|
|
+ if (_pongReceivedFromWebSocketServer) {
|
|
|
+ qCDebug(lcPushNotifications) << "Websocket respond with a pong in time.";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ qCInfo(lcPushNotifications) << "Websocket did not respond with a pong in time. Try to reconnect.";
|
|
|
+ // Try again to connect
|
|
|
+ setup();
|
|
|
+}
|
|
|
+
|
|
|
+void PushNotifications::setPingInterval(int timeoutInterval)
|
|
|
+{
|
|
|
+ _pingTimer.setInterval(timeoutInterval);
|
|
|
+ _pingTimedOutTimer.setInterval(timeoutInterval);
|
|
|
+}
|
|
|
+
|
|
|
+void PushNotifications::emitFilesChanged()
|
|
|
+{
|
|
|
+ emit filesChanged(_account);
|
|
|
+}
|
|
|
+
|
|
|
+void PushNotifications::emitNotificationsChanged()
|
|
|
+{
|
|
|
+ emit notificationsChanged(_account);
|
|
|
+}
|
|
|
+
|
|
|
+void PushNotifications::emitActivitiesChanged()
|
|
|
+{
|
|
|
emit activitiesChanged(_account);
|
|
|
}
|
|
|
}
|