C

Message Proxy: Testing Qt Safe Renderer Messaging Interface

// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial

// This file is part of the Qt Safe Renderer module
#include <QtNetwork/QTcpSocket>
#include <QtSafeRenderer/qsafeevent.h>
#include <qsafeeventsender.h>
#include "server.h"

Server::Server(const quint16 port, QObject *parent)
    : QObject(parent)
{
    runServer(port);
}

void Server::runServer(const quint16 port)
{
    m_tcpServer = new QTcpServer(this);
    if (!m_tcpServer->listen(QHostAddress::Any, port)) {
        qCritical() << "Unable to start the server: " << m_tcpServer->errorString();
        return;
    }

    connect(m_tcpServer, &QTcpServer::newConnection, this, &Server::newConnection);

    QString ipAddress;
    QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
    // use the first non-localhost IPv4 address
    for (int i = 0U; i < ipAddressesList.size(); ++i) {
        if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
            ipAddressesList.at(i).toIPv4Address()) {
            ipAddress = ipAddressesList.at(i).toString();
            break;
        }
    }
    // if we did not find one, use IPv4 localhost
    if (ipAddress.isEmpty())
        ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
    qDebug() << "The server is running on: " << ipAddress << ":" << m_tcpServer->serverPort();
}

void Server::newConnection()
{
    QTcpSocket *clientConnection = m_tcpServer->nextPendingConnection();

    // Initialize buffer for this client
    m_clientBuffers[clientConnection] = QByteArray();

    connect(clientConnection, &QAbstractSocket::disconnected,
            this, &Server::clientDisconnected);

    connect(clientConnection, &QAbstractSocket::disconnected,
            clientConnection, &QObject::deleteLater);

    connect(clientConnection, &QAbstractSocket::readyRead,
            this, &Server::readData);

}

void Server::clientDisconnected()
{
    QTcpSocket *clientConnection = qobject_cast<QTcpSocket *>(QObject::sender());
    if (clientConnection) {
        // Clean up the buffer for this client
        m_clientBuffers.remove(clientConnection);
    }
}

static bool sendReply(QTcpSocket &clientConnection, const SafeRenderer::QSafeEvent &reply)
{
    bool success = false;

    // Check if socket is still connected before attempting to write
    if (clientConnection.state() == QAbstractSocket::ConnectedState) {
        const quint64 bytesWritten = clientConnection.write(reinterpret_cast<const char*>(reply.rawData()), SafeRenderer::QSafeEvent::messageLength);
        if (bytesWritten == SafeRenderer::QSafeEvent::messageLength) {
            success = true;
        } else {
            qDebug() << "Failed to write reply to client"
                     << ", error:" << clientConnection.errorString();
        }
    }
    return success;
}

void Server::handleVerificationRequest(const SafeRenderer::QSafeEvent &request, QTcpSocket &clientConnection)
{
    bool success = false;
    SafeRenderer::QSafeEvent reply;
    SafeRenderer::QSafeEventSender::QSafeEventSenderStatus status = m_messageSender.sendEvent(request, reply);

    if (status == SafeRenderer::QSafeEventSender::Success) {
        if (sendReply(clientConnection, reply)) {
            success = true;
        }
    }

    if (!success) {
        qDebug() << "Failed to send request to renderer.";
    }
}

void Server::readData()
{
    QTcpSocket *clientConnection = qobject_cast<QTcpSocket *>(QObject::sender());
    if (clientConnection) {
        // Read all available data and append to client's buffer
        QByteArray newData = clientConnection->readAll();
        if (newData.size() > 0) {
            // Append to the existing buffer for this client
            m_clientBuffers[clientConnection].append(newData);
            QByteArray &buffer = m_clientBuffers[clientConnection];

            // Process all complete messages in the buffer
            quint8 dataBuffer[SafeRenderer::QSafeEvent::messageLength];
            while (buffer.size() >= SafeRenderer::QSafeEvent::messageLength) {
                // Copy one complete message
                std::copy(buffer.data(), buffer.data() + SafeRenderer::QSafeEvent::messageLength, dataBuffer);

                SafeRenderer::QSafeEvent safeEvent(dataBuffer);
                if (safeEvent.getEventId() == SafeRenderer::EventId::EventOutputVerificationStatusRequest) {
                    handleVerificationRequest(safeEvent, *clientConnection);
                } else {
                    m_messageSender.sendEvent(safeEvent);
                }
                // Remove the processed message from the buffer
                buffer.remove(0, SafeRenderer::QSafeEvent::messageLength);
            }
        }
    }
}