/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. * This project including source code and documentation is protected by copyright law, and * remains the property of nymea GmbH. All rights, including reproduction, publication, * editing and translation, are reserved. The use of this project is subject to the terms of a * license agreement to be concluded with nymea GmbH in accordance with the terms * of use of nymea GmbH, available under https://nymea.io/license * * GNU General Public License Usage * Alternatively, this project may be redistributed and/or modified under * the terms of the GNU General Public License as published by the Free Software Foundation, * GNU version 3. this project is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this project. * If not, see . * * For any further details and any questions please contact us under contact@nymea.io * or see our FAQ/Licensing Information on https://nymea.io/license/faq * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "websocketserver.h" #include "loggingcategories.h" #include namespace remoteproxy { WebSocketServer::WebSocketServer(bool sslEnabled, const QSslConfiguration &sslConfiguration, QObject *parent) : TransportInterface(parent), m_sslEnabled(sslEnabled), m_sslConfiguration(sslConfiguration) { m_serverName = "WebSocket"; } WebSocketServer::~WebSocketServer() { stopServer(); } bool WebSocketServer::running() const { if (!m_server) return false; return m_server->isListening(); } QSslConfiguration WebSocketServer::sslConfiguration() const { return m_sslConfiguration; } void WebSocketServer::sendData(const QUuid &clientId, const QByteArray &data) { QWebSocket *client = nullptr; client = m_clientList.value(clientId); if (client) { qCDebug(dcWebSocketServerTraffic()) << "--> Sending data to client:" << data; client->sendTextMessage(data); } else { qCWarning(dcWebSocketServer()) << "Client" << clientId << "unknown to this transport"; } } void WebSocketServer::killClientConnection(const QUuid &clientId, const QString &killReason) { QWebSocket *client = m_clientList.value(clientId); if (!client) return; qCWarning(dcWebSocketServer()) << "Killing client connection" << clientId.toString() << "Reason:" << killReason; client->flush(); client->close(QWebSocketProtocol::CloseCodeBadOperation, killReason); } uint WebSocketServer::connectionsCount() const { return m_clientList.count(); } void WebSocketServer::onClientConnected() { // Got a new client connected QWebSocket *client = m_server->nextPendingConnection(); if (!client) { qCWarning(dcWebSocketServer()) << "Next pending connection disappeared. Doing nothing."; return; } // Check websocket version if (client->version() != QWebSocketProtocol::Version13) { qCWarning(dcWebSocketServer()) << "Client with invalid protocol version" << client->version() << ". Rejecting."; client->close(QWebSocketProtocol::CloseCodeProtocolError, QString("invalid protocol version: %1 != Supported Version 13").arg(client->version())); client->deleteLater(); return; } // Create new uuid for this connection QUuid clientId = QUuid::createUuid(); qCDebug(dcWebSocketServer()) << "New client connected:" << client << client->peerAddress().toString() << clientId.toString(); // Append the new client to the client list m_clientList.insert(clientId, client); connect(client, SIGNAL(binaryMessageReceived(QByteArray)), this, SLOT(onBinaryMessageReceived(QByteArray))); connect(client, SIGNAL(textMessageReceived(QString)), this, SLOT(onTextMessageReceived(QString))); connect(client, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onClientError(QAbstractSocket::SocketError))); connect(client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected())); emit clientConnected(clientId, client->peerAddress()); } void WebSocketServer::onClientDisconnected() { QWebSocket *client = static_cast(sender()); QUuid clientId = m_clientList.key(client); qCDebug(dcWebSocketServer()) << "Client disconnected:" << client << client->peerAddress().toString() << clientId.toString() << client->closeReason(); // Manually close it in any case client->close(); m_clientList.take(clientId)->deleteLater(); emit clientDisconnected(clientId); } void WebSocketServer::onTextMessageReceived(const QString &message) { QWebSocket *client = static_cast(sender()); qCDebug(dcWebSocketServerTraffic()) << "Text message from" << client->peerAddress().toString() << ":" << message; emit dataAvailable(m_clientList.key(client), message.toUtf8()); } void WebSocketServer::onBinaryMessageReceived(const QByteArray &data) { QWebSocket *client = static_cast(sender()); qCWarning(dcWebSocketServerTraffic()) << "<-- Binary message from" << client->peerAddress().toString() << ":" << data; // Note: this is not expected, so close this client connection. client->close(QWebSocketProtocol::CloseCodeBadOperation, "Binary message not expected."); } void WebSocketServer::onClientError(QAbstractSocket::SocketError error) { QWebSocket *client = static_cast(sender()); qCWarning(dcWebSocketServer()) << "Client error occurred:" << client << client->peerAddress().toString() << error << client->errorString() << "Closing the socket."; // Note: on any error which can occurre, make sure the socket will be closed in any case client->close(); } void WebSocketServer::onAcceptError(QAbstractSocket::SocketError error) { qCWarning(dcWebSocketServer()) << "Server accept error occurred:" << error << m_server->errorString(); } void WebSocketServer::onServerError(QWebSocketProtocol::CloseCode closeCode) { qCWarning(dcWebSocketServer()) << "Server error occurred:" << closeCode << m_server->errorString(); } bool WebSocketServer::startServer() { if (m_sslEnabled) { m_server = new QWebSocketServer(QCoreApplication::applicationName(), QWebSocketServer::SecureMode, this); m_server->setSslConfiguration(sslConfiguration()); } else { m_server = new QWebSocketServer(QCoreApplication::applicationName(), QWebSocketServer::NonSecureMode, this); } connect (m_server, &QWebSocketServer::newConnection, this, &WebSocketServer::onClientConnected); connect (m_server, &QWebSocketServer::acceptError, this, &WebSocketServer::onAcceptError); connect (m_server, &QWebSocketServer::serverError, this, &WebSocketServer::onServerError); qCDebug(dcWebSocketServer()) << "Starting server" << m_server->serverName() << serverUrl().toString(); if (!m_server->listen(QHostAddress(m_serverUrl.host()), static_cast(serverUrl().port()))) { qCWarning(dcWebSocketServer()) << "Server" << m_server->serverName() << "could not listen on" << serverUrl().toString(); delete m_server; m_server = nullptr; return false; } qCDebug(dcWebSocketServer()) << "Server started successfully."; return true; } bool WebSocketServer::stopServer() { // Clean up client connections foreach (QWebSocket *client, m_clientList.values()) { client->close(QWebSocketProtocol::CloseCodeNormal, "Stop server"); client->flush(); client->abort(); } // Delete the server object if (!m_server) return true; qCDebug(dcWebSocketServer()) << "Stopping server" << m_server->serverName() << serverUrl().toString(); m_server->close(); delete m_server; m_server = nullptr; return true; } }