AusweisApp2/src/remote_device/WebSocketChannel.cpp

143 lines
3.3 KiB
C++

/*!
* \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany
*/
#include "WebSocketChannel.h"
#include "RemoteServiceSettings.h"
#include <QLoggingCategory>
#include <QThread>
Q_DECLARE_LOGGING_CATEGORY(remote_device)
using namespace governikus;
namespace
{
const int PING_PONG_TIMEOUT_MS = 5000;
}
QString WebSocketChannel::makeConnectionId(const QSharedPointer<QWebSocket>& pConnection)
{
if (pConnection)
{
return RemoteServiceSettings::generateFingerprint(pConnection->sslConfiguration().peerCertificate());
}
return QString();
}
WebSocketChannel::WebSocketChannel(const QSharedPointer<QWebSocket>& pConnection)
: mConnection(pConnection)
, mId(makeConnectionId(pConnection))
, mPingTimer()
, mPongTimer()
{
if (mConnection)
{
connect(mConnection.data(), &QWebSocket::textMessageReceived, this, &WebSocketChannel::onReceived);
connect(mConnection.data(), &QWebSocket::disconnected, this, &WebSocketChannel::onDisconnected);
connect(&mPingTimer, &QTimer::timeout, this, &WebSocketChannel::onPingScheduled);
connect(mConnection.data(), &QWebSocket::pong, this, &WebSocketChannel::onPongReceived);
connect(&mPongTimer, &QTimer::timeout, this, &WebSocketChannel::onPongTimeout);
mPingTimer.setInterval(PING_PONG_TIMEOUT_MS);
mPingTimer.setSingleShot(true);
mPongTimer.setInterval(PING_PONG_TIMEOUT_MS);
mPongTimer.setSingleShot(true);
mPingTimer.start();
}
}
WebSocketChannel::~WebSocketChannel()
{
if (mConnection)
{
disconnect(mConnection.data(), &QWebSocket::textMessageReceived, this, &WebSocketChannel::onReceived);
disconnect(mConnection.data(), &QWebSocket::disconnected, this, &WebSocketChannel::onDisconnected);
disconnect(mConnection.data(), &QWebSocket::pong, this, &WebSocketChannel::onPongReceived);
disconnect(&mPingTimer, &QTimer::timeout, this, &WebSocketChannel::onPingScheduled);
disconnect(&mPongTimer, &QTimer::timeout, this, &WebSocketChannel::onPongTimeout);
}
}
void WebSocketChannel::send(const QByteArray& pDataBlock)
{
if (mConnection)
{
mConnection->sendTextMessage(QString::fromUtf8(pDataBlock));
}
}
void WebSocketChannel::close()
{
if (mConnection)
{
// QWebSocket.close() must be executed in the thread in which the QWebSocket object lives.
if (mConnection->thread() == QThread::currentThread())
{
mConnection->close();
}
else
{
qCWarning(remote_device) << "Tried to close web socket from the wrong thread: skipping";
}
}
}
const QString& WebSocketChannel::getId() const
{
return mId;
}
void WebSocketChannel::onReceived(const QString& pMessage)
{
Q_EMIT fireReceived(pMessage.toUtf8());
}
void WebSocketChannel::onDisconnected()
{
mPingTimer.stop();
mPongTimer.stop();
Q_ASSERT(mConnection);
if (mConnection)
{
Q_EMIT fireClosed(mConnection->closeCode() == QWebSocketProtocol::CloseCodeNormal ?
GlobalStatus::Code::RemoteReader_CloseCode_NormalClose :
GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose);
}
}
void WebSocketChannel::onPingScheduled()
{
mConnection->ping();
mPongTimer.start();
}
void WebSocketChannel::onPongReceived()
{
mPongTimer.stop();
mPingTimer.start();
}
void WebSocketChannel::onPongTimeout()
{
qCDebug(remote_device) << "No pong received from remote for" << (PING_PONG_TIMEOUT_MS / 1000) << "seconds, closing socket";
close();
}