Implement webserver resource mechanism

This commit is contained in:
Simon Stürz 2025-11-05 21:05:34 +01:00
parent 88d1e4ce52
commit 40182978e0
20 changed files with 324 additions and 138 deletions

View File

@ -23,14 +23,12 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "nymeacore.h"
#include "servers/httpreply.h"
#include "nymeasettings.h"
#include "loggingcategories.h"
#include "debugserverhandler.h"
#include "nymeaconfiguration.h"
#include "version.h"
#include <QXmlStreamWriter>
#include <QCoreApplication>
#include <QMessageLogger>
#include <QJsonDocument>
@ -41,19 +39,43 @@
#include <QPair>
#include <QHostInfo>
namespace nymeaserver {
QList<QWebSocket*> DebugServerHandler::s_websocketClients;
QList<QWebSocket *> DebugServerHandler::s_websocketClients;
QMutex DebugServerHandler::s_loggingMutex;
DebugServerHandler::DebugServerHandler(QObject *parent) :
QObject(parent)
WebServerResource("/debug", parent)
{
connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::debugServerEnabledChanged, this, &DebugServerHandler::onDebugServerEnabledChanged);
onDebugServerEnabledChanged(NymeaCore::instance()->configuration()->debugServerEnabled());
}
bool DebugServerHandler::authenticationRequired() const
{
return false;
}
HttpReply *DebugServerHandler::processRequest(const HttpRequest &request)
{
if (NymeaCore::instance()->configuration()->debugServerEnabled()) {
// Verify methods
if (request.method() != HttpRequest::Get && request.method() != HttpRequest::Options) {
HttpReply *reply = HttpReply::createErrorReply(HttpReply::MethodNotAllowed);
reply->setHeader(HttpReply::AllowHeader, "GET, OPTIONS");
return reply;
}
qCDebug(dcDebugServer()) << "Request:" << request.url().toString();
return processDebugRequest(request.url().path(), request.urlQuery());
} else {
qCWarning(dcWebServer()) << "The debug server handler is disabled. You can enable it by adding \'debugServerEnabled=true\' in the \'nymead\' section of the nymead.conf file.";
return HttpReply::createErrorReply(HttpReply::NotFound);
}
}
HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, const QUrlQuery &requestQuery)
{
qCDebug(dcDebugServer()) << "Debug request for" << requestPath;

View File

@ -33,17 +33,19 @@
#include <QMutex>
#include "debugreportgenerator.h"
#include "servers/httpreply.h"
#include "webserver/webserverresource.h"
namespace nymeaserver {
class DebugServerHandler : public QObject
class DebugServerHandler : public WebServerResource
{
Q_OBJECT
public:
explicit DebugServerHandler(QObject *parent = nullptr);
HttpReply *processDebugRequest(const QString &requestPath, const QUrlQuery &requestQuery);
bool authenticationRequired() const override;
HttpReply *processRequest(const HttpRequest &request) override;
private:
static QList<QWebSocket*> s_websocketClients;
@ -63,6 +65,8 @@ private:
DebugReportGenerator *m_debugReportGenerator = nullptr;
HttpReply *processDebugRequest(const QString &requestPath, const QUrlQuery &requestQuery);
QByteArray loadResourceData(const QString &resourceFileName);
QString getResourceFileName(const QString &requestPath);
bool resourceFileExits(const QString &requestPath);

View File

@ -69,6 +69,7 @@ HEADERS += nymeacore.h \
logging/logengineinfluxdb.h \
scriptengine/scriptthing.h \
scriptengine/scriptthings.h \
servers/webserverresource.h \
zwave/zwavedevicedatabase.h \
zwave/zwavemanagerreply.h \
zwave/zwavenodeimplementation.h \
@ -107,8 +108,6 @@ HEADERS += nymeacore.h \
servers/tcpserver.h \
servers/mocktcpserver.h \
servers/webserver.h \
servers/httprequest.h \
servers/httpreply.h \
servers/bluetoothserver.h \
servers/websocketserver.h \
servers/mqttbroker.h \
@ -210,8 +209,6 @@ SOURCES += nymeacore.cpp \
servers/tcpserver.cpp \
servers/mocktcpserver.cpp \
servers/webserver.cpp \
servers/httprequest.cpp \
servers/httpreply.cpp \
servers/websocketserver.cpp \
servers/bluetoothserver.cpp \
servers/mqttbroker.cpp \

View File

@ -37,6 +37,7 @@
#include "jsonrpc/scriptshandler.h"
#include "jsonrpc/debughandler.h"
#include "usermanager/usermanager.h"
#include "debugserverhandler.h"
#include "version.h"
#include "integrations/thingmanagerimplementation.h"
@ -152,6 +153,7 @@ void NymeaCore::init(const QStringList &additionalInterfaces, bool disableLogEng
qCDebug(dcCore) << "Creating Debug Server Handler";
m_debugServerHandler = new DebugServerHandler(this);
m_serverManager->registerWebServerResource(m_debugServerHandler);
qCDebug(dcCore) << "Register Debug Handler";
m_serverManager->jsonServer()->registerHandler(new DebugHandler(m_serverManager->jsonServer()));

View File

@ -39,7 +39,6 @@
#include "time/timemanager.h"
#include "hardwaremanagerimplementation.h"
#include "debugserverhandler.h"
#include <QObject>
@ -63,6 +62,7 @@ class ZigbeeManager;
class ZWaveManager;
class ModbusRtuManager;
class SerialPortMonitor;
class DebugServerHandler;
namespace scriptengine {
class ScriptEngine;

View File

@ -56,6 +56,8 @@
#include "network/zeroconf/zeroconfservicepublisher.h"
#include <webserver/webserverresource.h>
#include <QSslCertificate>
#include <QSslConfiguration>
#include <QSslKey>
@ -213,6 +215,13 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati
foreach (const WebServerConfiguration &config, configuration->webServerConfigurations()) {
WebServer *webServer = new WebServer(config, m_sslConfiguration, this);
m_webServers.insert(config.id, webServer);
foreach (WebServerResource *resource, m_webServerResources) {
if (!webServer->registerResource(resource)) {
qCWarning(dcServerManager()) << "Unable to register resource" << resource->basePath() << "on webserver" << webServer->serverUrl().toString();
}
}
if (webServer->startServer()) {
registerZeroConfService(config, "http", "_http._tcp");
}
@ -267,6 +276,30 @@ MqttBroker *ServerManager::mqttBroker() const
return m_mqttBroker;
}
bool ServerManager::registerWebServerResource(WebServerResource *resource)
{
if (m_webServerResources.contains(resource->basePath())) {
qCDebug(dcServerManager()) << "Could not register web server resource" << resource->basePath() << "because a resource with this path has already been registered";
return false;
}
m_webServerResources.insert(resource->basePath(), resource);
foreach (WebServer *webserver, m_webServers)
webserver->registerResource(resource);
return true;
}
void ServerManager::unregisterWebServerResource(WebServerResource *resource)
{
m_webServerResources.remove(resource->basePath());
foreach (WebServer *webserver, m_webServers)
webserver->unregisterResource(resource);
}
void ServerManager::tcpServerConfigurationChanged(const QString &id)
{
ServerConfiguration config = NymeaCore::instance()->configuration()->tcpServerConfigurations().value(id);
@ -346,6 +379,12 @@ void ServerManager::webServerConfigurationChanged(const QString &id)
qCDebug(dcServerManager()) << "Received a Web Server config change event but don't have a Web Server instance for it. Creating new WebServer instance on" << config.address << config.port << "(SSL:" << config.sslEnabled << ")";
server = new WebServer(config, m_sslConfiguration, this);
m_webServers.insert(config.id, server);
foreach (WebServerResource *resource, m_webServerResources) {
if (!server->registerResource(resource)) {
qCWarning(dcServerManager()) << "Unable to register resource" << resource->basePath() << "on webserver" << server->serverUrl().toString();
}
}
}
if (server->startServer()) {
registerZeroConfService(config, "http", "_http._tcp");
@ -358,6 +397,7 @@ void ServerManager::webServerConfigurationRemoved(const QString &id)
qCWarning(dcServerManager()) << "Received a Web Server config removed event but don't have a Web Server instance for it.";
return;
}
WebServer *server = m_webServers.take(id);
unregisterZeroConfService(id, "http");
server->stopServer();

View File

@ -32,6 +32,7 @@
#include <QSslConfiguration>
#include <QSslKey>
class WebServerResource;
namespace nymeaserver {
@ -55,13 +56,14 @@ public:
// Interfaces
JsonRPCServerImplementation *jsonServer() const;
BluetoothServer* bluetoothServer() const;
BluetoothServer *bluetoothServer() const;
MockTcpServer *mockTcpServer() const;
MqttBroker *mqttBroker() const;
// Resources for the webservers
bool registerWebServerResource(WebServerResource *resource);
void unregisterWebServerResource(WebServerResource *resource);
public slots:
void setServerName(const QString &serverName);
@ -94,14 +96,16 @@ private:
JsonRPCServerImplementation *m_jsonServer;
BluetoothServer *m_bluetoothServer;
QHash<QString, TcpServer*> m_tcpServers;
QHash<QString, WebSocketServer*> m_webSocketServers;
QHash<QString, WebServer*> m_webServers;
QHash<QString, TcpServer *> m_tcpServers;
QHash<QString, WebSocketServer *> m_webSocketServers;
QHash<QString, WebServer *> m_webServers;
QHash<QString, TunnelProxyServer *> m_tunnelProxyServers;
MockTcpServer *m_mockTcpServer;
MqttBroker *m_mqttBroker;
QHash<QString, WebServerResource *> m_webServerResources;
// Encrytption and stuff
QSslConfiguration m_sslConfiguration;
QSslKey m_certificateKey;

View File

@ -76,11 +76,10 @@
#include "webserver.h"
#include "loggingcategories.h"
#include "nymeasettings.h"
#include "nymeacore.h"
#include "httpreply.h"
#include "httprequest.h"
#include "debugserverhandler.h"
#include "webserver/httpreply.h"
#include "webserver/httprequest.h"
#include "webserver/webserverresource.h"
#include "version.h"
#include <QRegularExpression>
@ -107,16 +106,20 @@ WebServer::WebServer(const WebServerConfiguration &configuration, const QSslConf
m_configuration(configuration),
m_sslConfiguration(sslConfiguration)
{
if (QCoreApplication::instance()->organizationName() == "nymea-test") {
if (QCoreApplication::instance()->organizationName() == "nymea-test")
m_configuration.publicFolder = QCoreApplication::applicationDirPath();
}
qCDebug(dcWebServer()) << "Starting WebServer. Interface:" << m_configuration.address << "Port:" << m_configuration.port << "SSL:" << m_configuration.sslEnabled << "AUTH:" << m_configuration.authenticationEnabled << "Public folder:" << QDir(m_configuration.publicFolder).canonicalPath();
qCInfo(dcWebServer()) << "Starting WebServer. Interface:" << m_configuration.address
<< "Port:" << m_configuration.port
<< "SSL:" << (m_configuration.sslEnabled ? "enabled" : "disabled")
<< "AUTH:" << (m_configuration.authenticationEnabled ? "enabled" : "disabled")
<< "Public folder:" << QDir(m_configuration.publicFolder).canonicalPath();
}
/*! Destructor of this \l{WebServer}. */
WebServer::~WebServer()
{
qCDebug(dcWebServer()) << "Shutting down \"Webserver\"" << serverUrl().toString();
qCInfo(dcWebServer()) << "Shutting down \"Webserver\"" << serverUrl().toString();
this->close();
}
@ -124,7 +127,17 @@ WebServer::~WebServer()
/*! Returns the server URL of this WebServer. */
QUrl WebServer::serverUrl() const
{
return QUrl(QString("%1://%2:%3").arg((m_configuration.sslEnabled ? "https" : "http")).arg(m_configuration.address).arg(m_configuration.port));
QUrl url;
url.setScheme(m_configuration.sslEnabled ? "https" : "http");
url.setHost(m_configuration.address);
url.setPort(m_configuration.port);
return url;
}
/*! Returns the configuration of this WebServer. */
WebServerConfiguration WebServer::configuration() const
{
return m_configuration;
}
/*! Send the given \a reply map to the corresponding client.
@ -133,7 +146,6 @@ QUrl WebServer::serverUrl() const
*/
void WebServer::sendHttpReply(HttpReply *reply)
{
// get the right socket
QSslSocket *socket = nullptr;
socket = m_clientList.value(reply->clientId());
if (!socket) {
@ -148,13 +160,40 @@ void WebServer::sendHttpReply(HttpReply *reply)
socket->write(reply->data());
}
QList<WebServerResource *> WebServer::resources() const
{
return m_resources.values();
}
bool WebServer::registerResource(WebServerResource *resource)
{
qCDebug(dcWebServer()) << "Register resource" << resource->basePath() << "on server" << serverUrl().toString();
if (m_resources.contains(resource->basePath())) {
qCWarning(dcWebServer()) << "Could not register resource" << resource->basePath() << "because there is already a resource resistered for this base path.";
return false;
}
m_resources.insert(resource->basePath(), resource);
return true;
}
void WebServer::unregisterResource(WebServerResource *resource)
{
qCDebug(dcWebServer()) << "Unregister resource" << resource->basePath() << "from server" << serverUrl().toString();
if (!m_resources.contains(resource->basePath())) {
qCWarning(dcWebServer()) << "Could not unregister resource" << resource->basePath() << "because there is no resource resistered with this base path.";
return;
}
m_resources.remove(resource->basePath());
}
bool WebServer::verifyFile(QSslSocket *socket, const QString &fileName)
{
QFileInfo file(fileName);
// make sure the file exists
if (!file.exists()) {
qCDebug(dcWebServer()) << "requested file" << file.filePath() << "does not exist.";
qCDebug(dcWebServer()) << "Requested file" << file.filePath() << "does not exist.";
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setClientId(m_clientList.key(socket));
sendHttpReply(reply);
@ -182,6 +221,7 @@ bool WebServer::verifyFile(QSslSocket *socket, const QString &fileName)
reply->deleteLater();
return false;
}
return true;
}
@ -269,16 +309,7 @@ void WebServer::incomingConnection(qintptr socketDescriptor)
return;
}
connect(socket, &QSslSocket::readyRead, this, &WebServer::readClient);
connect(socket, &QSslSocket::disconnected, this, &WebServer::onDisconnected);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
connect(socket, &QSslSocket::errorOccurred, this, &WebServer::onError);
#else
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
#endif
emit clientConnected(clientId);
setupClient(clientId, socket);
}
void WebServer::readClient()
@ -345,6 +376,29 @@ void WebServer::readClient()
}
}
// Verify if we habe a resource for this request
foreach (WebServerResource *resource, m_resources) {
if (request.url().path().startsWith(resource->basePath())) {
qCDebug(dcWebServer()) << "Let the resource handle this request";
qCDebug(dcDebugServer()) << "Request:" << request.url().toString();
HttpReply *reply = resource->processRequest(request);
reply->setClientId(clientId);
// Handle async replies
if (reply->type() == HttpReply::TypeAsync) {
connect(reply, &HttpReply::finished, this, &WebServer::onAsyncReplyFinished);
reply->startWait();
} else {
sendHttpReply(reply);
reply->deleteLater();
}
return;
}
}
// No resource handled this request, let the webserver itself handle it
// Verify method
if (request.method() == HttpRequest::Unhandled) {
HttpReply *reply = HttpReply::createErrorReply(HttpReply::MethodNotAllowed);
@ -364,43 +418,6 @@ void WebServer::readClient()
return;
}
// Check if this is a debug call
if (request.url().path().startsWith("/debug")) {
// Check if debug server is enabled
if (NymeaCore::instance()->configuration()->debugServerEnabled()) {
// Verify methods
if (request.method() != HttpRequest::Get && request.method() != HttpRequest::Options) {
HttpReply *reply = HttpReply::createErrorReply(HttpReply::MethodNotAllowed);
reply->setClientId(clientId);
reply->setHeader(HttpReply::AllowHeader, "GET, OPTIONS");
sendHttpReply(reply);
reply->deleteLater();
return;
}
qCDebug(dcDebugServer()) << "Request:" << request.url().toString();
HttpReply *reply = NymeaCore::instance()->debugServerHandler()->processDebugRequest(request.url().path(), request.urlQuery());
reply->setClientId(clientId);
// Handle async replies
if (reply->type() == HttpReply::TypeAsync) {
connect(reply, &HttpReply::finished, this, &WebServer::onAsyncReplyFinished);
reply->startWait();
} else {
sendHttpReply(reply);
reply->deleteLater();
}
return;
} else {
qCWarning(dcWebServer()) << "The debug server handler is disabled. You can enable it by adding \'debugServerEnabled=true\' in the \'nymead\' section of the nymead.conf file.";
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setClientId(clientId);
sendHttpReply(reply);
reply->deleteLater();
return;
}
}
// Check server.xml call
if (request.url().path() == "/server.xml" && request.method() == HttpRequest::Get) {
qCDebug(dcWebServer()) << "Server XML request call";
@ -413,7 +430,6 @@ void WebServer::readClient()
return;
}
// Request for a file...
if (request.method() == HttpRequest::Get) {
// Check if the webinterface dir does exist, otherwise a filerequest is not relevant
@ -508,15 +524,7 @@ void WebServer::onEncrypted()
{
QSslSocket* socket = static_cast<QSslSocket *>(sender());
qCDebug(dcWebServer()).noquote() << QString("Encrypted connection %1:%2 successfully established.").arg(socket->peerAddress().toString()).arg(socket->peerPort());
connect(socket, &QSslSocket::readyRead, this, &WebServer::readClient);
connect(socket, &QSslSocket::disconnected, this, &WebServer::onDisconnected);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
connect(socket, &QSslSocket::errorOccurred, this, &WebServer::onError);
#else
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
#endif
emit clientConnected(m_clientList.key(socket));
setupClient(m_clientList.key(socket), socket);
}
void WebServer::onError(QAbstractSocket::SocketError error)
@ -547,6 +555,20 @@ void WebServer::onAsyncReplyFinished()
reply->deleteLater();
}
void WebServer::setupClient(const QUuid &clientId, QSslSocket *socket)
{
connect(socket, &QSslSocket::readyRead, this, &WebServer::readClient);
connect(socket, &QSslSocket::disconnected, this, &WebServer::onDisconnected);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
connect(socket, &QSslSocket::errorOccurred, this, &WebServer::onError);
#else
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
#endif
emit clientConnected(clientId);
}
/*! Set the configuration of this \l{WebServer} to the given \a config.
*
* \sa WebServerConfiguration
@ -581,7 +603,7 @@ bool WebServer::startServer()
bool WebServer::stopServer()
{
foreach (QSslSocket *client, m_clientList.values())
client->close();
client->close();
close();
m_enabled = false;
@ -589,12 +611,6 @@ bool WebServer::stopServer()
return true;
}
WebServerConfiguration WebServer::configuration() const
{
return m_configuration;
}
QByteArray WebServer::createServerXmlDocument(QHostAddress address)
{
QByteArray uuid = NymeaCore::instance()->configuration()->serverUuid().toString().remove(QRegularExpression("[{}]")).toUtf8();
@ -612,9 +628,9 @@ QByteArray WebServer::createServerXmlDocument(QHostAddress address)
writer.writeEndElement(); // specVersion
QString presentationUrl = QString("%1://%2:%3")
.arg(m_configuration.sslEnabled ? "https" : "http")
.arg(address.toString())
.arg(m_configuration.port);
.arg(m_configuration.sslEnabled ? "https" : "http")
.arg(address.toString())
.arg(m_configuration.port);
writer.writeStartElement("device");
writer.writeTextElement("presentationURL", presentationUrl);
writer.writeTextElement("deviceType", "urn:schemas-upnp-org:device:Basic:1");
@ -815,8 +831,7 @@ void WebServerClient::removeConnection(QSslSocket *socket)
*/
void WebServerClient::resetTimout(QSslSocket *socket)
{
QTimer *timer = nullptr;
timer = m_runningConnections.key(socket);
QTimer *timer = m_runningConnections.key(socket);
if (timer)
timer->start();
}

View File

@ -43,10 +43,11 @@
// Note: Hypertext Transfer Protocol (HTTP/1.1) from the Internet Engineering Task Force (IETF):
// https://tools.ietf.org/html/rfc7231
namespace nymeaserver {
class HttpReply;
class HttpRequest;
class WebServerResource;
namespace nymeaserver {
class WebServerClient : public QObject
{
@ -80,9 +81,14 @@ public:
~WebServer() override;
QUrl serverUrl() const;
WebServerConfiguration configuration() const;
void sendHttpReply(HttpReply *reply);
QList<WebServerResource *> resources() const;
bool registerResource(WebServerResource *resource);
void unregisterResource(WebServerResource *resource);
private:
QHash<QUuid, QSslSocket *> m_clientList;
QList<WebServerClient *> m_webServerClients;
@ -92,6 +98,8 @@ private:
WebServerConfiguration m_configuration;
QSslConfiguration m_sslConfiguration;
QHash<QString, WebServerResource *> m_resources;
bool m_enabled = false;
bool verifyFile(QSslSocket *socket, const QString &fileName);
@ -99,16 +107,16 @@ private:
QByteArray createServerXmlDocument(QHostAddress address);
HttpReply *processIconRequest(const QString &fileName);
HttpReply *processDebugRequest(const QString &requestPath);
protected:
void incomingConnection(qintptr socketDescriptor) override;
signals:
void httpRequestReady(const QUuid &clientId, const HttpRequest &httpRequest);
void clientConnected(const QUuid &clientId);
void clientDisconnected(const QUuid &clientId);
void httpRequestReady(const QUuid &clientId, const HttpRequest &httpRequest);
private slots:
void readClient();
void onDisconnected();
@ -116,12 +124,14 @@ private slots:
void onError(QAbstractSocket::SocketError error);
void onAsyncReplyFinished();
void setupClient(const QUuid &clientId, QSslSocket *socket);
public slots:
void setConfiguration(const WebServerConfiguration &config);
void setConfiguration(const nymeaserver::WebServerConfiguration &config);
void setServerName(const QString &serverName);
bool startServer();
bool stopServer();
WebServerConfiguration configuration() const;
};

View File

@ -122,7 +122,7 @@ void ZWaveManager::loadZWaveNetworks()
NymeaSettings settings(NymeaSettings::SettingsRoleZWave);
qCDebug(dcZWave()) << "Loading ZWave networks from" << settings.fileName();
settings.beginGroup("Networks");
foreach (const QString &uuidString, settings.childGroups()) {
foreach (const QString &uuidString, settings.childGroups()) {
settings.beginGroup(uuidString);
QString serialPort = settings.value("serialPort").toString();
quint32 homeId = settings.value("homeId").toULongLong();

View File

@ -56,5 +56,4 @@ private:
Q_DECLARE_INTERFACE(ExperiencePlugin, "io.nymea.ExperiencePlugin")
#endif // EXPERIENCEPLUGIN_H

View File

@ -139,6 +139,9 @@ HEADERS += \
platform/platformupdatecontroller.h \
platform/platformzeroconfcontroller.h \
experiences/experienceplugin.h \
webserver/httprequest.h \
webserver/httpreply.h \
webserver/webserverresource.h \
SOURCES += \
hardware/modbus/modbusrtuhardwareresource.cpp \
@ -257,6 +260,9 @@ SOURCES += \
platform/platformupdatecontroller.cpp \
platform/platformzeroconfcontroller.cpp \
experiences/experienceplugin.cpp \
webserver/httprequest.cpp \
webserver/httpreply.cpp \
webserver/webserverresource.cpp \
RESOURCES += \

View File

@ -137,15 +137,12 @@
#include "httpreply.h"
#include "loggingcategories.h"
#include "nymeacore.h"
#include "version.h"
#include "../version.h"
#include <QDateTime>
#include <QPair>
#include <QDebug>
namespace nymeaserver {
HttpReply::HttpReply(QObject *parent) :
QObject(parent),
m_statusCode(HttpReply::Ok),
@ -162,7 +159,7 @@ HttpReply::HttpReply(QObject *parent) :
// set known headers
setHeader(HttpReply::ContentTypeHeader, "text/plain; charset=\"utf-8\";");
setHeader(HttpHeaderType::ServerHeader, "nymea/" + QByteArray(NYMEA_VERSION_STRING));
setHeader(HttpHeaderType::DateHeader, NymeaCore::instance()->timeManager()->currentDateTime().toString("ddd, dd MMM yyyy hh:mm:ss").toUtf8());
setHeader(HttpHeaderType::DateHeader, QDateTime::currentDateTime().toString("ddd, dd MMM yyyy hh:mm:ss").toUtf8());
setHeader(HttpHeaderType::CacheControlHeader, "no-cache");
setHeader(HttpHeaderType::ConnectionHeader, "Keep-Alive");
setRawHeader("Access-Control-Allow-Origin","*");
@ -185,7 +182,7 @@ HttpReply::HttpReply(const HttpReply::HttpStatusCode &statusCode, const HttpRepl
// set known / default headers
setHeader(HttpReply::ContentTypeHeader, "text/plain; charset=\"utf-8\";");
setHeader(HttpHeaderType::ServerHeader, "nymea/" + QByteArray(NYMEA_VERSION_STRING));
setHeader(HttpHeaderType::DateHeader, NymeaCore::instance()->timeManager()->currentDateTime().toString("ddd, dd MMM yyyy hh:mm:ss").toUtf8());
setHeader(HttpHeaderType::DateHeader, QDateTime::currentDateTime().toString("ddd, dd MMM yyyy hh:mm:ss").toUtf8());
setHeader(HttpHeaderType::CacheControlHeader, "no-cache");
setHeader(HttpHeaderType::ConnectionHeader, "Keep-Alive");
setRawHeader("Access-Control-Allow-Origin","*");
@ -490,5 +487,3 @@ QDebug operator<<(QDebug debug, HttpReply *httpReply)
debug << qUtf8Printable(httpReply->payload());
return debug;
}
}

View File

@ -33,13 +33,10 @@
// Note: RFC 7231 HTTP/1.1 Semantics and Content -> http://tools.ietf.org/html/rfc7231
namespace nymeaserver {
class HttpReply: public QObject
{
Q_OBJECT
public:
enum HttpStatusCode {
Ok = 200,
Created = 201,
@ -81,9 +78,9 @@ public:
HttpReply(QObject *parent = nullptr);
HttpReply(const HttpStatusCode &statusCode = HttpStatusCode::Ok, const Type &type = TypeSync, QObject *parent = nullptr);
static HttpReply* createSuccessReply();
static HttpReply* createErrorReply(const HttpReply::HttpStatusCode &statusCode);
static HttpReply* createAsyncReply();
static HttpReply *createSuccessReply();
static HttpReply *createErrorReply(const HttpReply::HttpStatusCode &statusCode);
static HttpReply *createAsyncReply();
void setHttpStatusCode(const HttpStatusCode &statusCode);
HttpStatusCode httpStatusCode() const;
@ -116,9 +113,9 @@ public:
bool timedOut() const;
private:
HttpStatusCode m_statusCode;
HttpStatusCode m_statusCode = HttpReply::Ok;
QByteArray m_reasonPhrase;
Type m_type;
Type m_type = HttpReply::TypeSync;
QUuid m_clientId;
QByteArray m_rawHeader;
@ -127,11 +124,11 @@ private:
QHash<QByteArray, QByteArray> m_rawHeaderList;
bool m_closeConnection;
bool m_closeConnection = false;
QTimer *m_timer = nullptr;
int m_timeout = 60000;
bool m_timedOut;
bool m_timedOut = false;
QByteArray getHttpReasonPhrase(const HttpStatusCode &statusCode);
QByteArray getHeaderType(const HttpHeaderType &headerType);
@ -149,6 +146,4 @@ signals:
QDebug operator<<(QDebug debug, HttpReply *httpReply);
}
#endif // HTTPREPLY_H

View File

@ -63,8 +63,6 @@
#include <QUrlQuery>
#include <QRegularExpression>
namespace nymeaserver {
/*! Construct an empty \l{HttpRequest}. */
HttpRequest::HttpRequest() :
m_rawData(QByteArray()),
@ -279,4 +277,3 @@ QDebug operator<<(QDebug debug, const HttpRequest &httpRequest)
return debug;
}
}

View File

@ -30,8 +30,6 @@
#include <QString>
#include <QHash>
namespace nymeaserver {
class HttpRequest
{
public:
@ -86,7 +84,6 @@ private:
RequestMethod getRequestMethodType(const QString &methodString);
};
QDebug operator<< (QDebug debug, const HttpRequest &httpRequest);
QDebug operator<<(QDebug debug, const HttpRequest &httpRequest);
}
#endif // HTTPREQUEST_H

View File

@ -0,0 +1,43 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2025, 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 <https://www.gnu.org/licenses/>.
*
* 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 "webserverresource.h"
WebServerResource::WebServerResource(const QString &basePath, QObject *parent)
: QObject{parent},
m_basePath{basePath}
{
}
QString WebServerResource::basePath() const
{
return m_basePath;
}

View File

@ -0,0 +1,58 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2025, 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 <https://www.gnu.org/licenses/>.
*
* 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
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef WebServerResource_H
#define WebServerResource_H
#include <QObject>
#include <QUrlQuery>
#include "httpreply.h"
#include "httprequest.h"
class WebServerResource : public QObject
{
Q_OBJECT
public:
explicit WebServerResource(const QString &basePath, QObject *parent = nullptr);
virtual ~WebServerResource() = default;
QString basePath() const;
virtual bool authenticationRequired() const = 0;
virtual HttpReply *processRequest(const HttpRequest &request) = 0;
protected:
QString m_basePath;
};
#endif // WebServerResource_H

View File

@ -41,8 +41,8 @@ class HttpDaemon : public QTcpServer
public:
HttpDaemon(Thing *thing, IntegrationPlugin* parent = nullptr);
~HttpDaemon();
void incomingConnection(qintptr socket) override;
void incomingConnection(qintptr socket) override;
void actionExecuted(const ActionTypeId &actionTypeId);
signals:

View File

@ -25,6 +25,8 @@
#include "nymeatestbase.h"
#include "nymeacore.h"
#include <webserver/httpreply.h>
#include <QXmlReader>
#include <QRegularExpression>