#include "lts_web_socket_client.h" #include #include #include #include "client_connection.h" namespace LtsWebSocket { class WebSocketHelper : public QObject { Q_OBJECT public: WebSocketHelper(AbstractClientMessageFactory *messageFactory, LtsWebSocketClient *webSocketClient, NGL::INGLLogger &logger); ~WebSocketHelper(); public Q_SLOTS: void doOpen(const QString &hostname, const unsigned short port); void doClose(); void doSendTextMessage(const QString &msg); void onConnected(); void onDisconnected(); void onError(QAbstractSocket::SocketError error); void parseMessage(const QString &message); Q_SIGNALS: void messageParsed(AbstractClientMessage *clientMessage); void connected(); void disconnected(); void dataReceived(); void error(QAbstractSocket::SocketError); private: AbstractClientMessageFactory *m_messageFactory; LtsWebSocketClient *m_webSocketClient; QWebSocket *m_socket; NGL::INGLLogger &m_logger; }; WebSocketHelper::WebSocketHelper(AbstractClientMessageFactory *messageFactory, LtsWebSocketClient *webSocketClient, NGL::INGLLogger &logger) : QObject() , m_messageFactory(messageFactory) , m_webSocketClient(webSocketClient) , m_socket(new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this)) , m_logger(logger) { connect(m_socket, &QWebSocket::textMessageReceived, this, &WebSocketHelper::parseMessage); connect(m_socket, &QWebSocket::textFrameReceived, this, &WebSocketHelper::dataReceived); connect(m_socket, &QWebSocket::connected, this, &WebSocketHelper::onConnected); connect(m_socket, &QWebSocket::disconnected, this, &WebSocketHelper::onDisconnected); connect(m_socket, QOverload::of(&QWebSocket::error), this, &WebSocketHelper::error); } WebSocketHelper::~WebSocketHelper() { m_socket->close(); delete m_socket; } void WebSocketHelper::doOpen(const QString &hostname, const unsigned short port) { const QUrl url(QStringLiteral("ws://%1:%2/").arg(hostname).arg(port)); if (m_socket->state() == QAbstractSocket::SocketState::ConnectedState) { if (m_socket->requestUrl() != url) doClose(); else { INFO_LOG(m_logger) << "Web socket to url: " << url.toDisplayString() << " is already open."; return; } } INFO_LOG(m_logger) << "Opening web socket to url: " << url.toDisplayString(); m_socket->open(url); } void WebSocketHelper::doClose() { INFO_LOG(m_logger) << "Closing web socket to url:" << m_socket->requestUrl().toDisplayString(); m_socket->close(); } void WebSocketHelper::doSendTextMessage(const QString &msg) { if (!m_socket || m_socket->state() != QAbstractSocket::ConnectedState) { ERROR_LOG(m_logger) << "Message could not be send, socket is not connected."; return; } DEBUG_LOG(m_logger) << "Sending message: " << msg; m_socket->sendTextMessage(msg); } void WebSocketHelper::onConnected() { INFO_LOG(m_logger) << "Connected to server url: " << m_socket->requestUrl().toDisplayString() << " using local port: " << m_socket->localPort(); emit connected(); } void WebSocketHelper::onDisconnected() { INFO_LOG(m_logger) << "Disconnected from server url: " << m_socket->requestUrl().toDisplayString(); emit disconnected(); } void WebSocketHelper::onError(QAbstractSocket::SocketError error) { ERROR_LOG(m_logger) << "Error occured: " << error << " closing socket."; m_socket->close(); } void WebSocketHelper::parseMessage(const QString &message) { const QString pingMsg(QStringLiteral("ping")); if (message.startsWith(pingMsg)) { doSendTextMessage(PongMessage(message.mid(pingMsg.size())).toJsonString()); return; } AbstractClientMessage *clientMessage = nullptr; auto result = m_messageFactory->parseMessage(message); if (QJsonParseError::NoError == result.error) clientMessage = m_messageFactory->createMessage(m_webSocketClient, message); else { WARNING_LOG(m_logger) << "Could not parse incoming message: '" << message << "', error: " << result.errorString(); clientMessage = new InvalidMessage(m_webSocketClient, QString(), QStringLiteral("Got message which is not JSON conform (%1)").arg(result.errorString())); } if (clientMessage) emit messageParsed(clientMessage); } LtsWebSocketClient::LtsWebSocketClient(AbstractClientMessageFactory *messageFactory, QObject *parent) : QObject(parent) , m_messageFactory(messageFactory) , m_webSocketHelperThread(new QThread(this)) , m_logger(getLogger("LtsWebSocketClient")) { m_webSocketHelper = new WebSocketHelper(m_messageFactory, this, m_logger); m_webSocketHelper->moveToThread(m_webSocketHelperThread); m_webSocketHelperThread->start(); connect(m_webSocketHelper, &WebSocketHelper::connected, this, &LtsWebSocketClient::connected); connect(m_webSocketHelper, &WebSocketHelper::disconnected, this, &LtsWebSocketClient::disconnected); connect(m_webSocketHelper, &WebSocketHelper::dataReceived, this, &LtsWebSocketClient::dataReceived); connect(m_webSocketHelper, &WebSocketHelper::messageParsed, this, &LtsWebSocketClient::onNewClientMessage); connect(this, &LtsWebSocketClient::onOpen, m_webSocketHelper, &WebSocketHelper::doOpen); connect(this, &LtsWebSocketClient::onClose, m_webSocketHelper, &WebSocketHelper::doClose); connect(this, &LtsWebSocketClient::onSendTextMessage, m_webSocketHelper, &WebSocketHelper::doSendTextMessage); } LtsWebSocketClient::~LtsWebSocketClient() { m_webSocketHelper->disconnect(this); m_webSocketHelperThread->quit(); m_webSocketHelperThread->wait(); delete m_webSocketHelper; } void LtsWebSocketClient::open(const QString &hostname, const unsigned short port) { emit onOpen(hostname, port); } void LtsWebSocketClient::close() { emit onClose(); } void LtsWebSocketClient::addReply(ReplyMessage *message) { const QString msg(message->toJsonString()); emit onSendTextMessage(msg); delete message; } void LtsWebSocketClient::onNewClientMessage(AbstractClientMessage *clientMessage) { if (!clientMessage) return; clientMessage->process(); delete clientMessage; } } #include "lts_web_socket_client.moc"