Merge PR #714: Extend webserver resource management
This commit is contained in:
commit
af3dc11276
@ -5,9 +5,10 @@ usr/include/nymea/experiences/*
|
|||||||
usr/include/nymea/hardware/*
|
usr/include/nymea/hardware/*
|
||||||
usr/include/nymea/integrations/*
|
usr/include/nymea/integrations/*
|
||||||
usr/include/nymea/jsonrpc/*
|
usr/include/nymea/jsonrpc/*
|
||||||
|
usr/include/nymea/logging/*
|
||||||
usr/include/nymea/network/*
|
usr/include/nymea/network/*
|
||||||
usr/include/nymea/platform/*
|
usr/include/nymea/platform/*
|
||||||
usr/include/nymea/time/*
|
usr/include/nymea/time/*
|
||||||
usr/include/nymea/types/*
|
usr/include/nymea/types/*
|
||||||
usr/include/nymea/logging/*
|
usr/include/nymea/webserver/*
|
||||||
usr/lib/@DEB_HOST_MULTIARCH@/pkgconfig/nymea.pc
|
usr/lib/@DEB_HOST_MULTIARCH@/pkgconfig/nymea.pc
|
||||||
|
|||||||
@ -23,14 +23,12 @@
|
|||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
#include "nymeacore.h"
|
#include "nymeacore.h"
|
||||||
#include "servers/httpreply.h"
|
|
||||||
#include "nymeasettings.h"
|
#include "nymeasettings.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
#include "debugserverhandler.h"
|
#include "debugserverhandler.h"
|
||||||
#include "nymeaconfiguration.h"
|
#include "nymeaconfiguration.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include <QXmlStreamWriter>
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QMessageLogger>
|
#include <QMessageLogger>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
@ -41,19 +39,38 @@
|
|||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <QHostInfo>
|
#include <QHostInfo>
|
||||||
|
|
||||||
|
|
||||||
namespace nymeaserver {
|
namespace nymeaserver {
|
||||||
|
|
||||||
QList<QWebSocket*> DebugServerHandler::s_websocketClients;
|
QList<QWebSocket *> DebugServerHandler::s_websocketClients;
|
||||||
QMutex DebugServerHandler::s_loggingMutex;
|
QMutex DebugServerHandler::s_loggingMutex;
|
||||||
|
|
||||||
DebugServerHandler::DebugServerHandler(QObject *parent) :
|
DebugServerHandler::DebugServerHandler(QObject *parent) :
|
||||||
QObject(parent)
|
WebServerResource("/debug", parent)
|
||||||
{
|
{
|
||||||
connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::debugServerEnabledChanged, this, &DebugServerHandler::onDebugServerEnabledChanged);
|
connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::debugServerEnabledChanged, this, &DebugServerHandler::onDebugServerEnabledChanged);
|
||||||
onDebugServerEnabledChanged(NymeaCore::instance()->configuration()->debugServerEnabled());
|
onDebugServerEnabledChanged(NymeaCore::instance()->configuration()->debugServerEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HttpReply *DebugServerHandler::processRequest(const HttpRequest &request)
|
||||||
|
{
|
||||||
|
if (m_enabled) {
|
||||||
|
|
||||||
|
// 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)
|
HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, const QUrlQuery &requestQuery)
|
||||||
{
|
{
|
||||||
qCDebug(dcDebugServer()) << "Debug request for" << requestPath;
|
qCDebug(dcDebugServer()) << "Debug request for" << requestPath;
|
||||||
@ -636,6 +653,8 @@ void DebugServerHandler::onDebugServerEnabledChanged(bool enabled)
|
|||||||
m_websocketServer = nullptr;
|
m_websocketServer = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugServerHandler::onWebsocketClientConnected()
|
void DebugServerHandler::onWebsocketClientConnected()
|
||||||
|
|||||||
@ -33,17 +33,17 @@
|
|||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
|
|
||||||
#include "debugreportgenerator.h"
|
#include "debugreportgenerator.h"
|
||||||
#include "servers/httpreply.h"
|
#include "webserver/webserverresource.h"
|
||||||
|
|
||||||
namespace nymeaserver {
|
namespace nymeaserver {
|
||||||
|
|
||||||
class DebugServerHandler : public QObject
|
class DebugServerHandler : public WebServerResource
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit DebugServerHandler(QObject *parent = nullptr);
|
explicit DebugServerHandler(QObject *parent = nullptr);
|
||||||
|
|
||||||
HttpReply *processDebugRequest(const QString &requestPath, const QUrlQuery &requestQuery);
|
HttpReply *processRequest(const HttpRequest &request) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QList<QWebSocket*> s_websocketClients;
|
static QList<QWebSocket*> s_websocketClients;
|
||||||
@ -63,6 +63,8 @@ private:
|
|||||||
|
|
||||||
DebugReportGenerator *m_debugReportGenerator = nullptr;
|
DebugReportGenerator *m_debugReportGenerator = nullptr;
|
||||||
|
|
||||||
|
HttpReply *processDebugRequest(const QString &requestPath, const QUrlQuery &requestQuery);
|
||||||
|
|
||||||
QByteArray loadResourceData(const QString &resourceFileName);
|
QByteArray loadResourceData(const QString &resourceFileName);
|
||||||
QString getResourceFileName(const QString &requestPath);
|
QString getResourceFileName(const QString &requestPath);
|
||||||
bool resourceFileExits(const QString &requestPath);
|
bool resourceFileExits(const QString &requestPath);
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include "experiencemanager.h"
|
#include "experiencemanager.h"
|
||||||
#include "experiences/experienceplugin.h"
|
#include "experiences/experienceplugin.h"
|
||||||
|
#include "servermanager.h"
|
||||||
|
|
||||||
#include "jsonrpc/jsonrpcserverimplementation.h"
|
#include "jsonrpc/jsonrpcserverimplementation.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
@ -35,9 +36,12 @@
|
|||||||
|
|
||||||
namespace nymeaserver {
|
namespace nymeaserver {
|
||||||
|
|
||||||
ExperienceManager::ExperienceManager(ThingManager *thingManager, JsonRPCServer *jsonRpcServer, QObject *parent) : QObject(parent),
|
ExperienceManager::ExperienceManager(ThingManager *thingManager, JsonRPCServer *jsonRpcServer, ServerManager *serverManager, QObject *parent) :
|
||||||
m_thingManager(thingManager),
|
QObject{parent},
|
||||||
m_jsonRpcServer(jsonRpcServer)
|
m_thingManager{thingManager},
|
||||||
|
m_jsonRpcServer{jsonRpcServer},
|
||||||
|
m_serverManager{serverManager}
|
||||||
|
|
||||||
{
|
{
|
||||||
staticMetaObject.invokeMethod(this, "loadPlugins", Qt::QueuedConnection);
|
staticMetaObject.invokeMethod(this, "loadPlugins", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
@ -122,6 +126,9 @@ void ExperienceManager::loadExperiencePlugin(const QString &file)
|
|||||||
plugin->setParent(this);
|
plugin->setParent(this);
|
||||||
plugin->initPlugin(m_thingManager, m_jsonRpcServer);
|
plugin->initPlugin(m_thingManager, m_jsonRpcServer);
|
||||||
|
|
||||||
|
if (plugin->webServerResource()) {
|
||||||
|
m_serverManager->registerWebServerResource(plugin->webServerResource());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExperienceManager::loadExperiencePlugin(ExperiencePlugin *experiencePlugin)
|
void ExperienceManager::loadExperiencePlugin(ExperiencePlugin *experiencePlugin)
|
||||||
@ -130,6 +137,10 @@ void ExperienceManager::loadExperiencePlugin(ExperiencePlugin *experiencePlugin)
|
|||||||
m_plugins.append(experiencePlugin);
|
m_plugins.append(experiencePlugin);
|
||||||
experiencePlugin->setParent(this);
|
experiencePlugin->setParent(this);
|
||||||
experiencePlugin->initPlugin(m_thingManager, m_jsonRpcServer);
|
experiencePlugin->initPlugin(m_thingManager, m_jsonRpcServer);
|
||||||
|
|
||||||
|
if (experiencePlugin->webServerResource()) {
|
||||||
|
m_serverManager->registerWebServerResource(experiencePlugin->webServerResource());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,11 +33,13 @@ class ThingManager;
|
|||||||
|
|
||||||
namespace nymeaserver {
|
namespace nymeaserver {
|
||||||
|
|
||||||
|
class ServerManager;
|
||||||
|
|
||||||
class ExperienceManager : public QObject
|
class ExperienceManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ExperienceManager(ThingManager *thingManager, JsonRPCServer *jsonRpcServer, QObject *parent = nullptr);
|
explicit ExperienceManager(ThingManager *thingManager, JsonRPCServer *jsonRpcServer, ServerManager *serverManager, QObject *parent = nullptr);
|
||||||
|
|
||||||
QList<ExperiencePlugin *> plugins() const;
|
QList<ExperiencePlugin *> plugins() const;
|
||||||
|
|
||||||
@ -50,6 +52,8 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
ThingManager *m_thingManager = nullptr;
|
ThingManager *m_thingManager = nullptr;
|
||||||
JsonRPCServer *m_jsonRpcServer = nullptr;
|
JsonRPCServer *m_jsonRpcServer = nullptr;
|
||||||
|
ServerManager *m_serverManager = nullptr;
|
||||||
|
|
||||||
QList<ExperiencePlugin *> m_plugins;
|
QList<ExperiencePlugin *> m_plugins;
|
||||||
|
|
||||||
QStringList pluginSearchDirs() const;
|
QStringList pluginSearchDirs() const;
|
||||||
|
|||||||
@ -107,8 +107,6 @@ HEADERS += nymeacore.h \
|
|||||||
servers/tcpserver.h \
|
servers/tcpserver.h \
|
||||||
servers/mocktcpserver.h \
|
servers/mocktcpserver.h \
|
||||||
servers/webserver.h \
|
servers/webserver.h \
|
||||||
servers/httprequest.h \
|
|
||||||
servers/httpreply.h \
|
|
||||||
servers/bluetoothserver.h \
|
servers/bluetoothserver.h \
|
||||||
servers/websocketserver.h \
|
servers/websocketserver.h \
|
||||||
servers/mqttbroker.h \
|
servers/mqttbroker.h \
|
||||||
@ -210,8 +208,6 @@ SOURCES += nymeacore.cpp \
|
|||||||
servers/tcpserver.cpp \
|
servers/tcpserver.cpp \
|
||||||
servers/mocktcpserver.cpp \
|
servers/mocktcpserver.cpp \
|
||||||
servers/webserver.cpp \
|
servers/webserver.cpp \
|
||||||
servers/httprequest.cpp \
|
|
||||||
servers/httpreply.cpp \
|
|
||||||
servers/websocketserver.cpp \
|
servers/websocketserver.cpp \
|
||||||
servers/bluetoothserver.cpp \
|
servers/bluetoothserver.cpp \
|
||||||
servers/mqttbroker.cpp \
|
servers/mqttbroker.cpp \
|
||||||
|
|||||||
@ -37,6 +37,7 @@
|
|||||||
#include "jsonrpc/scriptshandler.h"
|
#include "jsonrpc/scriptshandler.h"
|
||||||
#include "jsonrpc/debughandler.h"
|
#include "jsonrpc/debughandler.h"
|
||||||
#include "usermanager/usermanager.h"
|
#include "usermanager/usermanager.h"
|
||||||
|
#include "debugserverhandler.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include "integrations/thingmanagerimplementation.h"
|
#include "integrations/thingmanagerimplementation.h"
|
||||||
@ -152,12 +153,13 @@ void NymeaCore::init(const QStringList &additionalInterfaces, bool disableLogEng
|
|||||||
|
|
||||||
qCDebug(dcCore) << "Creating Debug Server Handler";
|
qCDebug(dcCore) << "Creating Debug Server Handler";
|
||||||
m_debugServerHandler = new DebugServerHandler(this);
|
m_debugServerHandler = new DebugServerHandler(this);
|
||||||
|
m_serverManager->registerWebServerResource(m_debugServerHandler);
|
||||||
|
|
||||||
qCDebug(dcCore) << "Register Debug Handler";
|
qCDebug(dcCore) << "Register Debug Handler";
|
||||||
m_serverManager->jsonServer()->registerHandler(new DebugHandler(m_serverManager->jsonServer()));
|
m_serverManager->jsonServer()->registerHandler(new DebugHandler(m_serverManager->jsonServer()));
|
||||||
|
|
||||||
qCDebug(dcCore()) << "Loading experiences";
|
qCDebug(dcCore()) << "Loading experiences";
|
||||||
m_experienceManager = new ExperienceManager(m_thingManager, m_serverManager->jsonServer(), this);
|
m_experienceManager = new ExperienceManager(m_thingManager, m_serverManager->jsonServer(), m_serverManager, this);
|
||||||
|
|
||||||
connect(m_configuration, &NymeaConfiguration::serverNameChanged, m_serverManager, &ServerManager::setServerName);
|
connect(m_configuration, &NymeaConfiguration::serverNameChanged, m_serverManager, &ServerManager::setServerName);
|
||||||
connect(m_thingManager, &ThingManagerImplementation::loaded, this, &NymeaCore::thingManagerLoaded);
|
connect(m_thingManager, &ThingManagerImplementation::loaded, this, &NymeaCore::thingManagerLoaded);
|
||||||
|
|||||||
@ -39,7 +39,6 @@
|
|||||||
#include "time/timemanager.h"
|
#include "time/timemanager.h"
|
||||||
#include "hardwaremanagerimplementation.h"
|
#include "hardwaremanagerimplementation.h"
|
||||||
|
|
||||||
#include "debugserverhandler.h"
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
@ -63,6 +62,7 @@ class ZigbeeManager;
|
|||||||
class ZWaveManager;
|
class ZWaveManager;
|
||||||
class ModbusRtuManager;
|
class ModbusRtuManager;
|
||||||
class SerialPortMonitor;
|
class SerialPortMonitor;
|
||||||
|
class DebugServerHandler;
|
||||||
|
|
||||||
namespace scriptengine {
|
namespace scriptengine {
|
||||||
class ScriptEngine;
|
class ScriptEngine;
|
||||||
|
|||||||
@ -56,6 +56,8 @@
|
|||||||
|
|
||||||
#include "network/zeroconf/zeroconfservicepublisher.h"
|
#include "network/zeroconf/zeroconfservicepublisher.h"
|
||||||
|
|
||||||
|
#include <webserver/webserverresource.h>
|
||||||
|
|
||||||
#include <QSslCertificate>
|
#include <QSslCertificate>
|
||||||
#include <QSslConfiguration>
|
#include <QSslConfiguration>
|
||||||
#include <QSslKey>
|
#include <QSslKey>
|
||||||
@ -214,6 +216,13 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati
|
|||||||
foreach (const WebServerConfiguration &config, configuration->webServerConfigurations()) {
|
foreach (const WebServerConfiguration &config, configuration->webServerConfigurations()) {
|
||||||
WebServer *webServer = new WebServer(config, m_sslConfiguration, this);
|
WebServer *webServer = new WebServer(config, m_sslConfiguration, this);
|
||||||
m_webServers.insert(config.id, webServer);
|
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()) {
|
if (webServer->startServer()) {
|
||||||
registerZeroConfService(config, "http", "_http._tcp");
|
registerZeroConfService(config, "http", "_http._tcp");
|
||||||
}
|
}
|
||||||
@ -268,6 +277,29 @@ MqttBroker *ServerManager::mqttBroker() const
|
|||||||
return m_mqttBroker;
|
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)
|
void ServerManager::tcpServerConfigurationChanged(const QString &id)
|
||||||
{
|
{
|
||||||
ServerConfiguration config = NymeaCore::instance()->configuration()->tcpServerConfigurations().value(id);
|
ServerConfiguration config = NymeaCore::instance()->configuration()->tcpServerConfigurations().value(id);
|
||||||
@ -347,6 +379,11 @@ 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 << ")";
|
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);
|
server = new WebServer(config, m_sslConfiguration, this);
|
||||||
m_webServers.insert(config.id, server);
|
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()) {
|
if (server->startServer()) {
|
||||||
registerZeroConfService(config, "http", "_http._tcp");
|
registerZeroConfService(config, "http", "_http._tcp");
|
||||||
@ -359,7 +396,12 @@ 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.";
|
qCWarning(dcServerManager()) << "Received a Web Server config removed event but don't have a Web Server instance for it.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebServer *server = m_webServers.take(id);
|
WebServer *server = m_webServers.take(id);
|
||||||
|
|
||||||
|
foreach (WebServerResource *resource, m_webServerResources)
|
||||||
|
server->unregisterResource(resource);
|
||||||
|
|
||||||
unregisterZeroConfService(id, "http");
|
unregisterZeroConfService(id, "http");
|
||||||
server->stopServer();
|
server->stopServer();
|
||||||
server->deleteLater();
|
server->deleteLater();
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
#include <QSslConfiguration>
|
#include <QSslConfiguration>
|
||||||
#include <QSslKey>
|
#include <QSslKey>
|
||||||
|
|
||||||
|
class WebServerResource;
|
||||||
|
|
||||||
namespace nymeaserver {
|
namespace nymeaserver {
|
||||||
|
|
||||||
@ -55,13 +56,14 @@ public:
|
|||||||
|
|
||||||
// Interfaces
|
// Interfaces
|
||||||
JsonRPCServerImplementation *jsonServer() const;
|
JsonRPCServerImplementation *jsonServer() const;
|
||||||
|
BluetoothServer *bluetoothServer() const;
|
||||||
BluetoothServer* bluetoothServer() const;
|
|
||||||
|
|
||||||
MockTcpServer *mockTcpServer() const;
|
MockTcpServer *mockTcpServer() const;
|
||||||
|
|
||||||
MqttBroker *mqttBroker() const;
|
MqttBroker *mqttBroker() const;
|
||||||
|
|
||||||
|
// Resources for the webservers
|
||||||
|
bool registerWebServerResource(WebServerResource *resource);
|
||||||
|
void unregisterWebServerResource(WebServerResource *resource);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setServerName(const QString &serverName);
|
void setServerName(const QString &serverName);
|
||||||
|
|
||||||
@ -94,14 +96,16 @@ private:
|
|||||||
JsonRPCServerImplementation *m_jsonServer;
|
JsonRPCServerImplementation *m_jsonServer;
|
||||||
|
|
||||||
BluetoothServer *m_bluetoothServer;
|
BluetoothServer *m_bluetoothServer;
|
||||||
QHash<QString, TcpServer*> m_tcpServers;
|
QHash<QString, TcpServer *> m_tcpServers;
|
||||||
QHash<QString, WebSocketServer*> m_webSocketServers;
|
QHash<QString, WebSocketServer *> m_webSocketServers;
|
||||||
QHash<QString, WebServer*> m_webServers;
|
QHash<QString, WebServer *> m_webServers;
|
||||||
QHash<QString, TunnelProxyServer *> m_tunnelProxyServers;
|
QHash<QString, TunnelProxyServer *> m_tunnelProxyServers;
|
||||||
MockTcpServer *m_mockTcpServer;
|
MockTcpServer *m_mockTcpServer;
|
||||||
|
|
||||||
MqttBroker *m_mqttBroker;
|
MqttBroker *m_mqttBroker;
|
||||||
|
|
||||||
|
QHash<QString, WebServerResource *> m_webServerResources;
|
||||||
|
|
||||||
// Encrytption and stuff
|
// Encrytption and stuff
|
||||||
QSslConfiguration m_sslConfiguration;
|
QSslConfiguration m_sslConfiguration;
|
||||||
QSslKey m_certificateKey;
|
QSslKey m_certificateKey;
|
||||||
|
|||||||
@ -76,11 +76,10 @@
|
|||||||
|
|
||||||
#include "webserver.h"
|
#include "webserver.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
#include "nymeasettings.h"
|
|
||||||
#include "nymeacore.h"
|
#include "nymeacore.h"
|
||||||
#include "httpreply.h"
|
#include "webserver/httpreply.h"
|
||||||
#include "httprequest.h"
|
#include "webserver/httprequest.h"
|
||||||
#include "debugserverhandler.h"
|
#include "webserver/webserverresource.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
@ -107,16 +106,20 @@ WebServer::WebServer(const WebServerConfiguration &configuration, const QSslConf
|
|||||||
m_configuration(configuration),
|
m_configuration(configuration),
|
||||||
m_sslConfiguration(sslConfiguration)
|
m_sslConfiguration(sslConfiguration)
|
||||||
{
|
{
|
||||||
if (QCoreApplication::instance()->organizationName() == "nymea-test") {
|
if (QCoreApplication::instance()->organizationName() == "nymea-test")
|
||||||
m_configuration.publicFolder = QCoreApplication::applicationDirPath();
|
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}. */
|
/*! Destructor of this \l{WebServer}. */
|
||||||
WebServer::~WebServer()
|
WebServer::~WebServer()
|
||||||
{
|
{
|
||||||
qCDebug(dcWebServer()) << "Shutting down \"Webserver\"" << serverUrl().toString();
|
qCInfo(dcWebServer()) << "Shutting down \"Webserver\"" << serverUrl().toString();
|
||||||
|
|
||||||
this->close();
|
this->close();
|
||||||
}
|
}
|
||||||
@ -124,7 +127,17 @@ WebServer::~WebServer()
|
|||||||
/*! Returns the server URL of this WebServer. */
|
/*! Returns the server URL of this WebServer. */
|
||||||
QUrl WebServer::serverUrl() const
|
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.
|
/*! Send the given \a reply map to the corresponding client.
|
||||||
@ -133,7 +146,6 @@ QUrl WebServer::serverUrl() const
|
|||||||
*/
|
*/
|
||||||
void WebServer::sendHttpReply(HttpReply *reply)
|
void WebServer::sendHttpReply(HttpReply *reply)
|
||||||
{
|
{
|
||||||
// get the right socket
|
|
||||||
QSslSocket *socket = nullptr;
|
QSslSocket *socket = nullptr;
|
||||||
socket = m_clientList.value(reply->clientId());
|
socket = m_clientList.value(reply->clientId());
|
||||||
if (!socket) {
|
if (!socket) {
|
||||||
@ -148,13 +160,40 @@ void WebServer::sendHttpReply(HttpReply *reply)
|
|||||||
socket->write(reply->data());
|
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)
|
bool WebServer::verifyFile(QSslSocket *socket, const QString &fileName)
|
||||||
{
|
{
|
||||||
QFileInfo file(fileName);
|
QFileInfo file(fileName);
|
||||||
|
|
||||||
// make sure the file exists
|
|
||||||
if (!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);
|
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
|
||||||
reply->setClientId(m_clientList.key(socket));
|
reply->setClientId(m_clientList.key(socket));
|
||||||
sendHttpReply(reply);
|
sendHttpReply(reply);
|
||||||
@ -182,6 +221,7 @@ bool WebServer::verifyFile(QSslSocket *socket, const QString &fileName)
|
|||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,16 +309,7 @@ void WebServer::incomingConnection(qintptr socketDescriptor)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(socket, &QSslSocket::readyRead, this, &WebServer::readClient);
|
setupClient(clientId, socket);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebServer::readClient()
|
void WebServer::readClient()
|
||||||
@ -345,6 +376,37 @@ void WebServer::readClient()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify if we habe a resource for this request
|
||||||
|
foreach (WebServerResource *resource, m_resources) {
|
||||||
|
if (request.url().path().startsWith(resource->basePath())) {
|
||||||
|
if (!resource->enabled()) {
|
||||||
|
qCDebug(dcWebServer()) << "The corresponding resource exists but is not enabled. Respond with 404 Not Found.";
|
||||||
|
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
|
||||||
|
reply->setClientId(clientId);
|
||||||
|
sendHttpReply(reply);
|
||||||
|
reply->deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// Verify method
|
||||||
if (request.method() == HttpRequest::Unhandled) {
|
if (request.method() == HttpRequest::Unhandled) {
|
||||||
HttpReply *reply = HttpReply::createErrorReply(HttpReply::MethodNotAllowed);
|
HttpReply *reply = HttpReply::createErrorReply(HttpReply::MethodNotAllowed);
|
||||||
@ -364,43 +426,6 @@ void WebServer::readClient()
|
|||||||
return;
|
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
|
// Check server.xml call
|
||||||
if (request.url().path() == "/server.xml" && request.method() == HttpRequest::Get) {
|
if (request.url().path() == "/server.xml" && request.method() == HttpRequest::Get) {
|
||||||
qCDebug(dcWebServer()) << "Server XML request call";
|
qCDebug(dcWebServer()) << "Server XML request call";
|
||||||
@ -413,7 +438,6 @@ void WebServer::readClient()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Request for a file...
|
// Request for a file...
|
||||||
if (request.method() == HttpRequest::Get) {
|
if (request.method() == HttpRequest::Get) {
|
||||||
// Check if the webinterface dir does exist, otherwise a filerequest is not relevant
|
// Check if the webinterface dir does exist, otherwise a filerequest is not relevant
|
||||||
@ -431,42 +455,12 @@ void WebServer::readClient()
|
|||||||
if (!verifyFile(socket, path))
|
if (!verifyFile(socket, path))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QFile file(path);
|
|
||||||
if (file.open(QFile::ReadOnly)) {
|
|
||||||
qCDebug(dcWebServer()) << "Load file" << file.fileName();
|
|
||||||
HttpReply *reply = HttpReply::createSuccessReply();
|
|
||||||
|
|
||||||
// Check content type
|
HttpReply *reply = WebServerResource::createFileReply(path);
|
||||||
if (file.fileName().endsWith(".html")) {
|
reply->setClientId(clientId);
|
||||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html; charset=\"utf-8\";");
|
sendHttpReply(reply);
|
||||||
} else if (file.fileName().endsWith(".css")) {
|
reply->deleteLater();
|
||||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/css; charset=\"utf-8\";");
|
|
||||||
} else if (file.fileName().endsWith(".pdf")) {
|
|
||||||
reply->setHeader(HttpReply::ContentTypeHeader, "application/pdf");
|
|
||||||
} else if (file.fileName().endsWith(".js")) {
|
|
||||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/javascript; charset=\"utf-8\";");
|
|
||||||
} else if (file.fileName().endsWith(".ttf")) {
|
|
||||||
reply->setHeader(HttpReply::ContentTypeHeader, "application/x-font-ttf");
|
|
||||||
} else if (file.fileName().endsWith(".eot")) {
|
|
||||||
reply->setHeader(HttpReply::ContentTypeHeader, "application/vnd.ms-fontobject");
|
|
||||||
} else if (file.fileName().endsWith(".woff")) {
|
|
||||||
reply->setHeader(HttpReply::ContentTypeHeader, "application/x-font-woff");
|
|
||||||
} else if (file.fileName().endsWith(".jpg") || file.fileName().endsWith(".jpeg")) {
|
|
||||||
reply->setHeader(HttpReply::ContentTypeHeader, "image/jpeg");
|
|
||||||
} else if (file.fileName().endsWith(".png") || file.fileName().endsWith(".PNG")) {
|
|
||||||
reply->setHeader(HttpReply::ContentTypeHeader, "image/png");
|
|
||||||
} else if (file.fileName().endsWith(".ico")) {
|
|
||||||
reply->setHeader(HttpReply::ContentTypeHeader, "image/x-icon");
|
|
||||||
} else if (file.fileName().endsWith(".svg")) {
|
|
||||||
reply->setHeader(HttpReply::ContentTypeHeader, "image/svg+xml; charset=\"utf-8\";");
|
|
||||||
}
|
|
||||||
|
|
||||||
reply->setPayload(file.readAll());
|
|
||||||
reply->setClientId(clientId);
|
|
||||||
sendHttpReply(reply);
|
|
||||||
reply->deleteLater();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reject everything else...
|
// Reject everything else...
|
||||||
@ -508,15 +502,7 @@ void WebServer::onEncrypted()
|
|||||||
{
|
{
|
||||||
QSslSocket* socket = static_cast<QSslSocket *>(sender());
|
QSslSocket* socket = static_cast<QSslSocket *>(sender());
|
||||||
qCDebug(dcWebServer()).noquote() << QString("Encrypted connection %1:%2 successfully established.").arg(socket->peerAddress().toString()).arg(socket->peerPort());
|
qCDebug(dcWebServer()).noquote() << QString("Encrypted connection %1:%2 successfully established.").arg(socket->peerAddress().toString()).arg(socket->peerPort());
|
||||||
connect(socket, &QSslSocket::readyRead, this, &WebServer::readClient);
|
setupClient(m_clientList.key(socket), socket);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebServer::onError(QAbstractSocket::SocketError error)
|
void WebServer::onError(QAbstractSocket::SocketError error)
|
||||||
@ -547,6 +533,20 @@ void WebServer::onAsyncReplyFinished()
|
|||||||
reply->deleteLater();
|
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.
|
/*! Set the configuration of this \l{WebServer} to the given \a config.
|
||||||
*
|
*
|
||||||
* \sa WebServerConfiguration
|
* \sa WebServerConfiguration
|
||||||
@ -581,7 +581,7 @@ bool WebServer::startServer()
|
|||||||
bool WebServer::stopServer()
|
bool WebServer::stopServer()
|
||||||
{
|
{
|
||||||
foreach (QSslSocket *client, m_clientList.values())
|
foreach (QSslSocket *client, m_clientList.values())
|
||||||
client->close();
|
client->close();
|
||||||
|
|
||||||
close();
|
close();
|
||||||
m_enabled = false;
|
m_enabled = false;
|
||||||
@ -589,12 +589,6 @@ bool WebServer::stopServer()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebServerConfiguration WebServer::configuration() const
|
|
||||||
{
|
|
||||||
return m_configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QByteArray WebServer::createServerXmlDocument(QHostAddress address)
|
QByteArray WebServer::createServerXmlDocument(QHostAddress address)
|
||||||
{
|
{
|
||||||
QByteArray uuid = NymeaCore::instance()->configuration()->serverUuid().toString().remove(QRegularExpression("[{}]")).toUtf8();
|
QByteArray uuid = NymeaCore::instance()->configuration()->serverUuid().toString().remove(QRegularExpression("[{}]")).toUtf8();
|
||||||
@ -612,9 +606,9 @@ QByteArray WebServer::createServerXmlDocument(QHostAddress address)
|
|||||||
writer.writeEndElement(); // specVersion
|
writer.writeEndElement(); // specVersion
|
||||||
|
|
||||||
QString presentationUrl = QString("%1://%2:%3")
|
QString presentationUrl = QString("%1://%2:%3")
|
||||||
.arg(m_configuration.sslEnabled ? "https" : "http")
|
.arg(m_configuration.sslEnabled ? "https" : "http")
|
||||||
.arg(address.toString())
|
.arg(address.toString())
|
||||||
.arg(m_configuration.port);
|
.arg(m_configuration.port);
|
||||||
writer.writeStartElement("device");
|
writer.writeStartElement("device");
|
||||||
writer.writeTextElement("presentationURL", presentationUrl);
|
writer.writeTextElement("presentationURL", presentationUrl);
|
||||||
writer.writeTextElement("deviceType", "urn:schemas-upnp-org:device:Basic:1");
|
writer.writeTextElement("deviceType", "urn:schemas-upnp-org:device:Basic:1");
|
||||||
@ -815,8 +809,7 @@ void WebServerClient::removeConnection(QSslSocket *socket)
|
|||||||
*/
|
*/
|
||||||
void WebServerClient::resetTimout(QSslSocket *socket)
|
void WebServerClient::resetTimout(QSslSocket *socket)
|
||||||
{
|
{
|
||||||
QTimer *timer = nullptr;
|
QTimer *timer = m_runningConnections.key(socket);
|
||||||
timer = m_runningConnections.key(socket);
|
|
||||||
if (timer)
|
if (timer)
|
||||||
timer->start();
|
timer->start();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,10 +43,11 @@
|
|||||||
// Note: Hypertext Transfer Protocol (HTTP/1.1) from the Internet Engineering Task Force (IETF):
|
// Note: Hypertext Transfer Protocol (HTTP/1.1) from the Internet Engineering Task Force (IETF):
|
||||||
// https://tools.ietf.org/html/rfc7231
|
// https://tools.ietf.org/html/rfc7231
|
||||||
|
|
||||||
namespace nymeaserver {
|
|
||||||
|
|
||||||
class HttpReply;
|
class HttpReply;
|
||||||
class HttpRequest;
|
class HttpRequest;
|
||||||
|
class WebServerResource;
|
||||||
|
|
||||||
|
namespace nymeaserver {
|
||||||
|
|
||||||
class WebServerClient : public QObject
|
class WebServerClient : public QObject
|
||||||
{
|
{
|
||||||
@ -80,9 +81,14 @@ public:
|
|||||||
~WebServer() override;
|
~WebServer() override;
|
||||||
|
|
||||||
QUrl serverUrl() const;
|
QUrl serverUrl() const;
|
||||||
|
WebServerConfiguration configuration() const;
|
||||||
|
|
||||||
void sendHttpReply(HttpReply *reply);
|
void sendHttpReply(HttpReply *reply);
|
||||||
|
|
||||||
|
QList<WebServerResource *> resources() const;
|
||||||
|
bool registerResource(WebServerResource *resource);
|
||||||
|
void unregisterResource(WebServerResource *resource);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QHash<QUuid, QSslSocket *> m_clientList;
|
QHash<QUuid, QSslSocket *> m_clientList;
|
||||||
QList<WebServerClient *> m_webServerClients;
|
QList<WebServerClient *> m_webServerClients;
|
||||||
@ -92,6 +98,8 @@ private:
|
|||||||
WebServerConfiguration m_configuration;
|
WebServerConfiguration m_configuration;
|
||||||
QSslConfiguration m_sslConfiguration;
|
QSslConfiguration m_sslConfiguration;
|
||||||
|
|
||||||
|
QHash<QString, WebServerResource *> m_resources;
|
||||||
|
|
||||||
bool m_enabled = false;
|
bool m_enabled = false;
|
||||||
|
|
||||||
bool verifyFile(QSslSocket *socket, const QString &fileName);
|
bool verifyFile(QSslSocket *socket, const QString &fileName);
|
||||||
@ -99,16 +107,16 @@ private:
|
|||||||
|
|
||||||
QByteArray createServerXmlDocument(QHostAddress address);
|
QByteArray createServerXmlDocument(QHostAddress address);
|
||||||
HttpReply *processIconRequest(const QString &fileName);
|
HttpReply *processIconRequest(const QString &fileName);
|
||||||
HttpReply *processDebugRequest(const QString &requestPath);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void incomingConnection(qintptr socketDescriptor) override;
|
void incomingConnection(qintptr socketDescriptor) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void httpRequestReady(const QUuid &clientId, const HttpRequest &httpRequest);
|
|
||||||
void clientConnected(const QUuid &clientId);
|
void clientConnected(const QUuid &clientId);
|
||||||
void clientDisconnected(const QUuid &clientId);
|
void clientDisconnected(const QUuid &clientId);
|
||||||
|
|
||||||
|
void httpRequestReady(const QUuid &clientId, const HttpRequest &httpRequest);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void readClient();
|
void readClient();
|
||||||
void onDisconnected();
|
void onDisconnected();
|
||||||
@ -116,12 +124,14 @@ private slots:
|
|||||||
void onError(QAbstractSocket::SocketError error);
|
void onError(QAbstractSocket::SocketError error);
|
||||||
void onAsyncReplyFinished();
|
void onAsyncReplyFinished();
|
||||||
|
|
||||||
|
void setupClient(const QUuid &clientId, QSslSocket *socket);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setConfiguration(const WebServerConfiguration &config);
|
void setConfiguration(const nymeaserver::WebServerConfiguration &config);
|
||||||
void setServerName(const QString &serverName);
|
void setServerName(const QString &serverName);
|
||||||
bool startServer();
|
bool startServer();
|
||||||
bool stopServer();
|
bool stopServer();
|
||||||
WebServerConfiguration configuration() const;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -631,6 +631,7 @@ bool UserManager::verifyToken(const QByteArray &token)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//qCDebug(dcUserManager) << "Token authorized for user" << result.value("username").toString();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -122,7 +122,7 @@ void ZWaveManager::loadZWaveNetworks()
|
|||||||
NymeaSettings settings(NymeaSettings::SettingsRoleZWave);
|
NymeaSettings settings(NymeaSettings::SettingsRoleZWave);
|
||||||
qCDebug(dcZWave()) << "Loading ZWave networks from" << settings.fileName();
|
qCDebug(dcZWave()) << "Loading ZWave networks from" << settings.fileName();
|
||||||
settings.beginGroup("Networks");
|
settings.beginGroup("Networks");
|
||||||
foreach (const QString &uuidString, settings.childGroups()) {
|
foreach (const QString &uuidString, settings.childGroups()) {
|
||||||
settings.beginGroup(uuidString);
|
settings.beginGroup(uuidString);
|
||||||
QString serialPort = settings.value("serialPort").toString();
|
QString serialPort = settings.value("serialPort").toString();
|
||||||
quint32 homeId = settings.value("homeId").toULongLong();
|
quint32 homeId = settings.value("homeId").toULongLong();
|
||||||
|
|||||||
@ -36,6 +36,13 @@ void ExperiencePlugin::init()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! This method will can be used to provide a web server resource to the core.
|
||||||
|
Override this method and provide an object. The resource will be added to the webserver after the init() method has been called. */
|
||||||
|
WebServerResource *ExperiencePlugin::webServerResource() const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Returns a pointer to the DeviceManager. The pointer won't be valid unless init() has been called. */
|
/*! Returns a pointer to the DeviceManager. The pointer won't be valid unless init() has been called. */
|
||||||
ThingManager *ExperiencePlugin::thingManager()
|
ThingManager *ExperiencePlugin::thingManager()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -29,8 +29,10 @@
|
|||||||
|
|
||||||
class ThingManager;
|
class ThingManager;
|
||||||
class JsonRPCServer;
|
class JsonRPCServer;
|
||||||
|
class WebServerResource;
|
||||||
|
|
||||||
namespace nymeaserver {
|
namespace nymeaserver {
|
||||||
|
|
||||||
class ExperienceManager;
|
class ExperienceManager;
|
||||||
}
|
}
|
||||||
class ExperiencePlugin : public QObject
|
class ExperiencePlugin : public QObject
|
||||||
@ -38,12 +40,15 @@ class ExperiencePlugin : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ExperiencePlugin(QObject *parent = nullptr);
|
explicit ExperiencePlugin(QObject *parent = nullptr);
|
||||||
|
virtual ~ExperiencePlugin() = default;
|
||||||
|
|
||||||
virtual void init() = 0;
|
virtual void init() = 0;
|
||||||
|
|
||||||
|
virtual WebServerResource *webServerResource() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ThingManager* thingManager();
|
ThingManager *thingManager();
|
||||||
JsonRPCServer* jsonRpcServer();
|
JsonRPCServer *jsonRpcServer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class nymeaserver::ExperienceManager;
|
friend class nymeaserver::ExperienceManager;
|
||||||
@ -56,5 +61,4 @@ private:
|
|||||||
|
|
||||||
Q_DECLARE_INTERFACE(ExperiencePlugin, "io.nymea.ExperiencePlugin")
|
Q_DECLARE_INTERFACE(ExperiencePlugin, "io.nymea.ExperiencePlugin")
|
||||||
|
|
||||||
|
|
||||||
#endif // EXPERIENCEPLUGIN_H
|
#endif // EXPERIENCEPLUGIN_H
|
||||||
|
|||||||
@ -317,6 +317,15 @@ States Thing::states() const
|
|||||||
return m_states;
|
return m_states;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Returns true, a \l{Param} with the given \a paramTypeId exists for this thing. */
|
||||||
|
bool Thing::hasParam(const QString ¶mName) const
|
||||||
|
{
|
||||||
|
ParamTypeId paramTypeId = m_thingClass.paramTypes().findByName(paramName).id();
|
||||||
|
return m_params.hasParam(paramTypeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Returns true, a \l{Param} with the given \a paramTypeId exists for this thing. */
|
/*! Returns true, a \l{Param} with the given \a paramTypeId exists for this thing. */
|
||||||
bool Thing::hasParam(const ParamTypeId ¶mTypeId) const
|
bool Thing::hasParam(const ParamTypeId ¶mTypeId) const
|
||||||
{
|
{
|
||||||
|
|||||||
@ -112,6 +112,7 @@ public:
|
|||||||
|
|
||||||
ParamList params() const;
|
ParamList params() const;
|
||||||
bool hasParam(const ParamTypeId ¶mTypeId) const;
|
bool hasParam(const ParamTypeId ¶mTypeId) const;
|
||||||
|
bool hasParam(const QString ¶mName) const;
|
||||||
void setParams(const ParamList ¶ms);
|
void setParams(const ParamList ¶ms);
|
||||||
|
|
||||||
QVariant paramValue(const ParamTypeId ¶mTypeId) const;
|
QVariant paramValue(const ParamTypeId ¶mTypeId) const;
|
||||||
|
|||||||
@ -139,6 +139,9 @@ HEADERS += \
|
|||||||
platform/platformupdatecontroller.h \
|
platform/platformupdatecontroller.h \
|
||||||
platform/platformzeroconfcontroller.h \
|
platform/platformzeroconfcontroller.h \
|
||||||
experiences/experienceplugin.h \
|
experiences/experienceplugin.h \
|
||||||
|
webserver/httprequest.h \
|
||||||
|
webserver/httpreply.h \
|
||||||
|
webserver/webserverresource.h \
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
hardware/modbus/modbusrtuhardwareresource.cpp \
|
hardware/modbus/modbusrtuhardwareresource.cpp \
|
||||||
@ -257,6 +260,9 @@ SOURCES += \
|
|||||||
platform/platformupdatecontroller.cpp \
|
platform/platformupdatecontroller.cpp \
|
||||||
platform/platformzeroconfcontroller.cpp \
|
platform/platformzeroconfcontroller.cpp \
|
||||||
experiences/experienceplugin.cpp \
|
experiences/experienceplugin.cpp \
|
||||||
|
webserver/httprequest.cpp \
|
||||||
|
webserver/httpreply.cpp \
|
||||||
|
webserver/webserverresource.cpp \
|
||||||
|
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
|
|||||||
@ -137,15 +137,12 @@
|
|||||||
|
|
||||||
#include "httpreply.h"
|
#include "httpreply.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
#include "nymeacore.h"
|
#include "../version.h"
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
namespace nymeaserver {
|
|
||||||
|
|
||||||
HttpReply::HttpReply(QObject *parent) :
|
HttpReply::HttpReply(QObject *parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
m_statusCode(HttpReply::Ok),
|
m_statusCode(HttpReply::Ok),
|
||||||
@ -162,7 +159,7 @@ HttpReply::HttpReply(QObject *parent) :
|
|||||||
// set known headers
|
// set known headers
|
||||||
setHeader(HttpReply::ContentTypeHeader, "text/plain; charset=\"utf-8\";");
|
setHeader(HttpReply::ContentTypeHeader, "text/plain; charset=\"utf-8\";");
|
||||||
setHeader(HttpHeaderType::ServerHeader, "nymea/" + QByteArray(NYMEA_VERSION_STRING));
|
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::CacheControlHeader, "no-cache");
|
||||||
setHeader(HttpHeaderType::ConnectionHeader, "Keep-Alive");
|
setHeader(HttpHeaderType::ConnectionHeader, "Keep-Alive");
|
||||||
setRawHeader("Access-Control-Allow-Origin","*");
|
setRawHeader("Access-Control-Allow-Origin","*");
|
||||||
@ -185,7 +182,7 @@ HttpReply::HttpReply(const HttpReply::HttpStatusCode &statusCode, const HttpRepl
|
|||||||
// set known / default headers
|
// set known / default headers
|
||||||
setHeader(HttpReply::ContentTypeHeader, "text/plain; charset=\"utf-8\";");
|
setHeader(HttpReply::ContentTypeHeader, "text/plain; charset=\"utf-8\";");
|
||||||
setHeader(HttpHeaderType::ServerHeader, "nymea/" + QByteArray(NYMEA_VERSION_STRING));
|
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::CacheControlHeader, "no-cache");
|
||||||
setHeader(HttpHeaderType::ConnectionHeader, "Keep-Alive");
|
setHeader(HttpHeaderType::ConnectionHeader, "Keep-Alive");
|
||||||
setRawHeader("Access-Control-Allow-Origin","*");
|
setRawHeader("Access-Control-Allow-Origin","*");
|
||||||
@ -200,6 +197,14 @@ HttpReply *HttpReply::createSuccessReply()
|
|||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HttpReply *HttpReply::createJsonReply(const QJsonDocument &jsonDoc, const HttpReply::HttpStatusCode &statusCode)
|
||||||
|
{
|
||||||
|
HttpReply *reply = new HttpReply(statusCode, HttpReply::TypeSync);
|
||||||
|
reply->setPayload(jsonDoc.toJson(QJsonDocument::Compact));
|
||||||
|
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
HttpReply *HttpReply::createErrorReply(const HttpReply::HttpStatusCode &statusCode)
|
HttpReply *HttpReply::createErrorReply(const HttpReply::HttpStatusCode &statusCode)
|
||||||
{
|
{
|
||||||
HttpReply *reply = new HttpReply(statusCode, HttpReply::TypeSync);
|
HttpReply *reply = new HttpReply(statusCode, HttpReply::TypeSync);
|
||||||
@ -392,6 +397,12 @@ QByteArray HttpReply::getHttpReasonPhrase(const HttpReply::HttpStatusCode &statu
|
|||||||
case BadRequest:
|
case BadRequest:
|
||||||
response = QString("Bad Request").toUtf8();
|
response = QString("Bad Request").toUtf8();
|
||||||
break;
|
break;
|
||||||
|
case Unauthorized:
|
||||||
|
response = QString("Unauthorized").toUtf8();
|
||||||
|
break;
|
||||||
|
case PaymentRequired:
|
||||||
|
response = QString("Payment required").toUtf8();
|
||||||
|
break;
|
||||||
case Forbidden:
|
case Forbidden:
|
||||||
response = QString("Forbidden").toUtf8();
|
response = QString("Forbidden").toUtf8();
|
||||||
break;
|
break;
|
||||||
@ -401,6 +412,9 @@ QByteArray HttpReply::getHttpReasonPhrase(const HttpReply::HttpStatusCode &statu
|
|||||||
case MethodNotAllowed:
|
case MethodNotAllowed:
|
||||||
response = QString("Method Not Allowed").toUtf8();
|
response = QString("Method Not Allowed").toUtf8();
|
||||||
break;
|
break;
|
||||||
|
case NotAcceptable:
|
||||||
|
response = QString("Not Acceptable").toUtf8();
|
||||||
|
break;
|
||||||
case RequestTimeout:
|
case RequestTimeout:
|
||||||
response = QString("Request Timeout").toUtf8();
|
response = QString("Request Timeout").toUtf8();
|
||||||
break;
|
break;
|
||||||
@ -490,5 +504,3 @@ QDebug operator<<(QDebug debug, HttpReply *httpReply)
|
|||||||
debug << qUtf8Printable(httpReply->payload());
|
debug << qUtf8Printable(httpReply->payload());
|
||||||
return debug;
|
return debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
@ -30,16 +30,14 @@
|
|||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
// Note: RFC 7231 HTTP/1.1 Semantics and Content -> http://tools.ietf.org/html/rfc7231
|
// Note: RFC 7231 HTTP/1.1 Semantics and Content -> http://tools.ietf.org/html/rfc7231
|
||||||
|
|
||||||
namespace nymeaserver {
|
|
||||||
|
|
||||||
class HttpReply: public QObject
|
class HttpReply: public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum HttpStatusCode {
|
enum HttpStatusCode {
|
||||||
Ok = 200,
|
Ok = 200,
|
||||||
Created = 201,
|
Created = 201,
|
||||||
@ -48,9 +46,12 @@ public:
|
|||||||
Found = 302,
|
Found = 302,
|
||||||
PermanentRedirect = 308,
|
PermanentRedirect = 308,
|
||||||
BadRequest = 400,
|
BadRequest = 400,
|
||||||
|
Unauthorized = 401,
|
||||||
|
PaymentRequired = 402,
|
||||||
Forbidden = 403,
|
Forbidden = 403,
|
||||||
NotFound = 404,
|
NotFound = 404,
|
||||||
MethodNotAllowed = 405,
|
MethodNotAllowed = 405,
|
||||||
|
NotAcceptable = 406,
|
||||||
RequestTimeout = 408,
|
RequestTimeout = 408,
|
||||||
Conflict = 409,
|
Conflict = 409,
|
||||||
InternalServerError = 500,
|
InternalServerError = 500,
|
||||||
@ -81,9 +82,10 @@ public:
|
|||||||
HttpReply(QObject *parent = nullptr);
|
HttpReply(QObject *parent = nullptr);
|
||||||
HttpReply(const HttpStatusCode &statusCode = HttpStatusCode::Ok, const Type &type = TypeSync, QObject *parent = nullptr);
|
HttpReply(const HttpStatusCode &statusCode = HttpStatusCode::Ok, const Type &type = TypeSync, QObject *parent = nullptr);
|
||||||
|
|
||||||
static HttpReply* createSuccessReply();
|
static HttpReply *createSuccessReply();
|
||||||
static HttpReply* createErrorReply(const HttpReply::HttpStatusCode &statusCode);
|
static HttpReply *createErrorReply(const HttpReply::HttpStatusCode &statusCode);
|
||||||
static HttpReply* createAsyncReply();
|
static HttpReply *createJsonReply(const QJsonDocument &jsonDoc, const HttpReply::HttpStatusCode &statusCode = HttpStatusCode::Ok);
|
||||||
|
static HttpReply *createAsyncReply();
|
||||||
|
|
||||||
void setHttpStatusCode(const HttpStatusCode &statusCode);
|
void setHttpStatusCode(const HttpStatusCode &statusCode);
|
||||||
HttpStatusCode httpStatusCode() const;
|
HttpStatusCode httpStatusCode() const;
|
||||||
@ -116,9 +118,9 @@ public:
|
|||||||
bool timedOut() const;
|
bool timedOut() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HttpStatusCode m_statusCode;
|
HttpStatusCode m_statusCode = HttpReply::Ok;
|
||||||
QByteArray m_reasonPhrase;
|
QByteArray m_reasonPhrase;
|
||||||
Type m_type;
|
Type m_type = HttpReply::TypeSync;
|
||||||
QUuid m_clientId;
|
QUuid m_clientId;
|
||||||
|
|
||||||
QByteArray m_rawHeader;
|
QByteArray m_rawHeader;
|
||||||
@ -127,11 +129,11 @@ private:
|
|||||||
|
|
||||||
QHash<QByteArray, QByteArray> m_rawHeaderList;
|
QHash<QByteArray, QByteArray> m_rawHeaderList;
|
||||||
|
|
||||||
bool m_closeConnection;
|
bool m_closeConnection = false;
|
||||||
|
|
||||||
QTimer *m_timer = nullptr;
|
QTimer *m_timer = nullptr;
|
||||||
int m_timeout = 60000;
|
int m_timeout = 60000;
|
||||||
bool m_timedOut;
|
bool m_timedOut = false;
|
||||||
|
|
||||||
QByteArray getHttpReasonPhrase(const HttpStatusCode &statusCode);
|
QByteArray getHttpReasonPhrase(const HttpStatusCode &statusCode);
|
||||||
QByteArray getHeaderType(const HttpHeaderType &headerType);
|
QByteArray getHeaderType(const HttpHeaderType &headerType);
|
||||||
@ -149,6 +151,4 @@ signals:
|
|||||||
|
|
||||||
QDebug operator<<(QDebug debug, HttpReply *httpReply);
|
QDebug operator<<(QDebug debug, HttpReply *httpReply);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // HTTPREPLY_H
|
#endif // HTTPREPLY_H
|
||||||
@ -63,8 +63,6 @@
|
|||||||
#include <QUrlQuery>
|
#include <QUrlQuery>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
namespace nymeaserver {
|
|
||||||
|
|
||||||
/*! Construct an empty \l{HttpRequest}. */
|
/*! Construct an empty \l{HttpRequest}. */
|
||||||
HttpRequest::HttpRequest() :
|
HttpRequest::HttpRequest() :
|
||||||
m_rawData(QByteArray()),
|
m_rawData(QByteArray()),
|
||||||
@ -279,4 +277,3 @@ QDebug operator<<(QDebug debug, const HttpRequest &httpRequest)
|
|||||||
return debug;
|
return debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
@ -30,8 +30,6 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
|
||||||
namespace nymeaserver {
|
|
||||||
|
|
||||||
class HttpRequest
|
class HttpRequest
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -86,7 +84,6 @@ private:
|
|||||||
RequestMethod getRequestMethodType(const QString &methodString);
|
RequestMethod getRequestMethodType(const QString &methodString);
|
||||||
};
|
};
|
||||||
|
|
||||||
QDebug operator<< (QDebug debug, const HttpRequest &httpRequest);
|
QDebug operator<<(QDebug debug, const HttpRequest &httpRequest);
|
||||||
|
|
||||||
}
|
|
||||||
#endif // HTTPREQUEST_H
|
#endif // HTTPREQUEST_H
|
||||||
102
libnymea/webserver/webserverresource.cpp
Normal file
102
libnymea/webserver/webserverresource.cpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* 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"
|
||||||
|
#include "loggingcategories.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
WebServerResource::WebServerResource(const QString &basePath, QObject *parent)
|
||||||
|
: QObject{parent},
|
||||||
|
m_basePath{basePath}
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WebServerResource::basePath() const
|
||||||
|
{
|
||||||
|
return m_basePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebServerResource::enabled() const
|
||||||
|
{
|
||||||
|
return m_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebServerResource::setEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (m_enabled == enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qCDebug(dcWebServer()) << "The resource" << m_basePath << "is now" << (enabled ? "enabled" : "disabled");
|
||||||
|
m_enabled = enabled;
|
||||||
|
emit enabledChanged(m_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpReply *WebServerResource::createFileReply(const QString fileName)
|
||||||
|
{
|
||||||
|
qCDebug(dcWebServer()) << "Create file reply for" << fileName;
|
||||||
|
|
||||||
|
QFile file(fileName);
|
||||||
|
if (!file.open(QFile::ReadOnly)) {
|
||||||
|
qCWarning(dcWebServer()) << "Unable to generate file reply. The file" << fileName << "could not be opened. Respond with 403 Forbidden.";
|
||||||
|
return HttpReply::createErrorReply(HttpReply::Forbidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpReply *reply = HttpReply::createSuccessReply();
|
||||||
|
|
||||||
|
// Check content type
|
||||||
|
if (file.fileName().endsWith(".html")) {
|
||||||
|
reply->setHeader(HttpReply::ContentTypeHeader, "text/html; charset=\"utf-8\";");
|
||||||
|
} else if (file.fileName().endsWith(".css")) {
|
||||||
|
reply->setHeader(HttpReply::ContentTypeHeader, "text/css; charset=\"utf-8\";");
|
||||||
|
} else if (file.fileName().endsWith(".pdf")) {
|
||||||
|
reply->setHeader(HttpReply::ContentTypeHeader, "application/pdf");
|
||||||
|
} else if (file.fileName().endsWith(".js")) {
|
||||||
|
reply->setHeader(HttpReply::ContentTypeHeader, "text/javascript; charset=\"utf-8\";");
|
||||||
|
} else if (file.fileName().endsWith(".ttf")) {
|
||||||
|
reply->setHeader(HttpReply::ContentTypeHeader, "application/x-font-ttf");
|
||||||
|
} else if (file.fileName().endsWith(".eot")) {
|
||||||
|
reply->setHeader(HttpReply::ContentTypeHeader, "application/vnd.ms-fontobject");
|
||||||
|
} else if (file.fileName().endsWith(".woff")) {
|
||||||
|
reply->setHeader(HttpReply::ContentTypeHeader, "application/x-font-woff");
|
||||||
|
} else if (file.fileName().endsWith(".jpg") || file.fileName().endsWith(".jpeg")) {
|
||||||
|
reply->setHeader(HttpReply::ContentTypeHeader, "image/jpeg");
|
||||||
|
} else if (file.fileName().endsWith(".png") || file.fileName().endsWith(".PNG")) {
|
||||||
|
reply->setHeader(HttpReply::ContentTypeHeader, "image/png");
|
||||||
|
} else if (file.fileName().endsWith(".ico")) {
|
||||||
|
reply->setHeader(HttpReply::ContentTypeHeader, "image/x-icon");
|
||||||
|
} else if (file.fileName().endsWith(".svg")) {
|
||||||
|
reply->setHeader(HttpReply::ContentTypeHeader, "image/svg+xml; charset=\"utf-8\";");
|
||||||
|
}
|
||||||
|
|
||||||
|
reply->setPayload(file.readAll());
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
65
libnymea/webserver/webserverresource.h
Normal file
65
libnymea/webserver/webserverresource.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
bool enabled() const;
|
||||||
|
void setEnabled(bool enabled);
|
||||||
|
|
||||||
|
virtual HttpReply *processRequest(const HttpRequest &request) = 0;
|
||||||
|
|
||||||
|
static HttpReply *createFileReply(const QString fileName);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void enabledChanged(bool enabled);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString m_basePath;
|
||||||
|
bool m_enabled = true;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WebServerResource_H
|
||||||
@ -41,8 +41,8 @@ class HttpDaemon : public QTcpServer
|
|||||||
public:
|
public:
|
||||||
HttpDaemon(Thing *thing, IntegrationPlugin* parent = nullptr);
|
HttpDaemon(Thing *thing, IntegrationPlugin* parent = nullptr);
|
||||||
~HttpDaemon();
|
~HttpDaemon();
|
||||||
void incomingConnection(qintptr socket) override;
|
|
||||||
|
|
||||||
|
void incomingConnection(qintptr socket) override;
|
||||||
void actionExecuted(const ActionTypeId &actionTypeId);
|
void actionExecuted(const ActionTypeId &actionTypeId);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|||||||
@ -25,6 +25,8 @@
|
|||||||
#include "nymeatestbase.h"
|
#include "nymeatestbase.h"
|
||||||
#include "nymeacore.h"
|
#include "nymeacore.h"
|
||||||
|
|
||||||
|
#include <webserver/httpreply.h>
|
||||||
|
|
||||||
#include <QXmlReader>
|
#include <QXmlReader>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user