diff --git a/webserver/main.cpp b/webserver/main.cpp index b9a7b64..3fa8739 100644 --- a/webserver/main.cpp +++ b/webserver/main.cpp @@ -1,121 +1,21 @@ #include #include -#include -#include #include -#include -#include - -#include -#include #include "utils/jsonutils.h" -#include "webplugin.h" -#include "weblistener.h" -#include "webapplication.h" + +#include "webserver.h" int main(int argc, char **argv) { QCoreApplication app(argc, argv); QCoreApplication::setApplicationName("webserver"); - QHash plugins; - - { - QDir dir(QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(QStringLiteral("plugins/") + QCoreApplication::applicationName())); - for(const auto &fileInfo : dir.entryInfoList(QDir::Files | QDir::NoSymLinks)) - { - if(!QLibrary::isLibrary(fileInfo.filePath())) - { - qWarning() << "skipping" << fileInfo.fileName() << "because no QLibrary"; - continue; // to skip windows junk files - } - - QPluginLoader pluginLoader(fileInfo.filePath()); - if(!pluginLoader.load()) - { - qCritical() << "error loading plugin" << fileInfo.fileName() << "because" << pluginLoader.errorString(); - continue; - } - - if(auto plugin = qobject_cast(pluginLoader.instance())) - { - const auto pluginName = plugin->pluginName(); - if(plugins.contains(pluginName)) - throw std::runtime_error(QString("duplicate plugin %0").arg(pluginName).toStdString()); - plugins.insert(pluginName, plugin); - } - else - qCritical() << "plugin" << fileInfo.fileName() << "could not be casted to WebPlugin"; - } - } - - QHash applications; - const auto configPath = QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(QCoreApplication::applicationName() + QStringLiteral(".json")); const auto config = getJson(configPath); - if(!config.contains(QStringLiteral("applications"))) - throw std::runtime_error("settings does not contain a applications"); - - { - const auto applicationsVal = config.value(QStringLiteral("applications")); - if(!applicationsVal.isObject()) - throw std::runtime_error("applications is not a json object"); - - const auto applicationsList = applicationsVal.toObject(); - for(auto iter = applicationsList.constBegin(); iter != applicationsList.constEnd(); iter++) - { - const auto applicationVal = iter.value(); - if(!applicationVal.isObject()) - throw std::runtime_error(QString("application %0 is not a json object").arg(iter.key()).toStdString()); - - auto application = applicationVal.toObject(); - if(!application.contains(QStringLiteral("_pluginName"))) - throw std::runtime_error(QString("application %0 does not contain a _pluginName").arg(iter.key()).toStdString()); - - const auto pluginNameVal = application.take(QStringLiteral("_pluginName")); - if(!pluginNameVal.isString()) - throw std::runtime_error(QString("application %0 pluginName is not a string").arg(iter.key()).toStdString()); - - auto pluginName = pluginNameVal.toString(); - - const auto pluginsIter = plugins.find(pluginName); - if(pluginsIter == plugins.constEnd()) - throw std::runtime_error(QString("application %0 references not installed plugin %1").arg(iter.key(), pluginName).toStdString()); - - applications.insert(iter.key(), pluginsIter.value()->createApplication(application)); - } - } - - QList listeners; - - if(!config.contains(QStringLiteral("listeners"))) - throw std::runtime_error("settings does not contain a listeners"); - - { - const auto listenersVal = config.value(QStringLiteral("listeners")); - if(!listenersVal.isArray()) - throw std::runtime_error("listeners is not a json array"); - - const auto listenersList = listenersVal.toArray(); - for(auto iter = listenersList.constBegin(); iter != listenersList.constEnd(); iter++) - { - const auto listenerVal = *iter; - if(!listenerVal.isObject()) - throw std::runtime_error(QString("listener %0 is not an object").arg(std::distance(listenersList.constBegin(), iter)).toStdString()); - - const auto listener = listenerVal.toObject(); - - listeners.append(new WebListener(listener, applications)); - } - } - - for(auto iter = applications.constBegin(); iter != applications.constEnd(); iter++) - iter.value()->start(); - - for(auto listener : listeners) - listener->start(); + WebServer server(config); + server.start(); return app.exec(); } diff --git a/webserver/webserver.json b/webserver/webserver.json index 31d3790..aa0c58d 100644 --- a/webserver/webserver.json +++ b/webserver/webserver.json @@ -58,7 +58,7 @@ }, "WifiLamp": { "_pluginName": "wifilamp", - "controlHostAdress": "QHostAddress::Any", + "controlHostAddress": "QHostAddress::Any", "controlPort": 1234 } }, @@ -67,26 +67,26 @@ "port": 8080, "vhosts": { "*": "Fallback", - "1000serien.com": "1000serien.com", - "www.1000serien.com": "1000serien.com", - "cdn.1000serien.com": "cdn.1000serien.com", - "brunner.ninja": "brunner.ninja", - "www.brunner.ninja": "brunner.ninja", - "telegram.brunner.ninja": "telegram.brunner.ninja", - "transmission.brunner.ninja": "transmission.brunner.ninja", - "findtheinvisiblegspot.com": "findtheinvisiblegspot.com", - "www.findtheinvisiblegspot.com": "findtheinvisiblegspot.com", - "flucky.xyz": "flucky.xyz", - "www.flucky.xyz": "flucky.xyz", - "mail.flucky.xyz": "mail.flucky.xyz", - "phpmyadmin.flucky.xyz": "phpmyadmin.flucky.xyz", - "localhorst.xyz": "localhorst.xyz", - "www.localhorst.xyz": "localhorst.xyz", - "maik-mahlow.de": "maik-mahlow.de", - "www.maik-mahlow.de": "maik-mahlow.de", - "flucky-server": "HelloWorld", - "192.168.0.2": "HelloWorld", - "lampen": "WifiLamp" + "1000serien.com:8080": "1000serien.com", + "www.1000serien.com:8080": "1000serien.com", + "cdn.1000serien.com:8080": "cdn.1000serien.com", + "brunner.ninja:8080": "brunner.ninja", + "www.brunner.ninja:8080": "brunner.ninja", + "telegram.brunner.ninja:8080": "telegram.brunner.ninja", + "transmission.brunner.ninja:8080": "transmission.brunner.ninja", + "findtheinvisiblegspot.com:8080": "findtheinvisiblegspot.com", + "www.findtheinvisiblegspot.com:8080": "findtheinvisiblegspot.com", + "flucky.xyz:8080": "flucky.xyz", + "www.flucky.xyz:8080": "flucky.xyz", + "mail.flucky.xyz:8080": "mail.flucky.xyz", + "phpmyadmin.flucky.xyz:8080": "phpmyadmin.flucky.xyz", + "localhorst.xyz:8080": "localhorst.xyz", + "www.localhorst.xyz:8080": "localhorst.xyz", + "maik-mahlow.de:8080": "maik-mahlow.de", + "www.maik-mahlow.de:8080": "maik-mahlow.de", + "flucky-server:8080": "HelloWorld", + "192.168.0.2:8080": "HelloWorld", + "lampen:8080": "WifiLamp" } }] } diff --git a/webserverlib/httpclientconnection.cpp b/webserverlib/httpclientconnection.cpp index 53c74f6..63574a9 100644 --- a/webserverlib/httpclientconnection.cpp +++ b/webserverlib/httpclientconnection.cpp @@ -6,10 +6,10 @@ #include "weblistener.h" -HttpClientConnection::HttpClientConnection(QTcpSocket &socket, WebListener &httpServer) : - QObject(&httpServer), +HttpClientConnection::HttpClientConnection(QTcpSocket &socket, WebListener &webServer) : + QObject(&webServer), m_socket(socket), - m_webListener(httpServer), + m_webListener(webServer), m_state(RequestLine), m_bodyLength(-1) { @@ -105,7 +105,11 @@ void HttpClientConnection::readyRead() case RequestLine: { auto parts = line.split(' '); - Q_ASSERT(parts.count() == 3); + if(parts.count() != 3) + { + m_socket.close(); + return; + } m_request.method = parts.at(0); m_request.path = parts.at(1); diff --git a/webserverlib/httpclientconnection.h b/webserverlib/httpclientconnection.h index e94a402..90c2649 100644 --- a/webserverlib/httpclientconnection.h +++ b/webserverlib/httpclientconnection.h @@ -19,7 +19,7 @@ class WEBSERVERLIB_EXPORT HttpClientConnection : public QObject Q_OBJECT public: - explicit HttpClientConnection(QTcpSocket &socket, WebListener &WebListener); + explicit HttpClientConnection(QTcpSocket &socket, WebListener &webListener); void sendResponse(const HttpResponse &response); void sendResponse(HttpResponse response, const QByteArray &byteArray); diff --git a/webserverlib/weblistener.cpp b/webserverlib/weblistener.cpp index 52da686..fd305f8 100644 --- a/webserverlib/weblistener.cpp +++ b/webserverlib/weblistener.cpp @@ -8,11 +8,13 @@ #include #include "utils/netutils.h" + +#include "webserver.h" #include "httpclientconnection.h" #include "webapplication.h" -WebListener::WebListener(const QJsonObject &config, const QHash &applications, QObject *parent) : - QObject(parent) +WebListener::WebListener(const QJsonObject &config, WebServer &webServer) : + QObject(&webServer), m_webServer(webServer) { if(!config.contains(QStringLiteral("hostAddress"))) throw std::runtime_error("listener does not contain hostAddress"); @@ -21,7 +23,7 @@ WebListener::WebListener(const QJsonObject &config, const QHashlisten(m_address, m_port)) + if(!m_server->listen(m_hostAddress, m_port)) throw std::runtime_error(QString("Could not start listening on %0:%1 because %2") - .arg(m_address.toString()).arg(m_port).arg(m_tcpServer->errorString()).toStdString()); + .arg(m_hostAddress.toString()).arg(m_port).arg(m_server->errorString()).toStdString()); - connect(m_tcpServer, &QTcpServer::acceptError, this, &WebListener::acceptError); - connect(m_tcpServer, &QTcpServer::newConnection, this, &WebListener::newConnection); + connect(m_server, &QTcpServer::acceptError, this, &WebListener::acceptError); + connect(m_server, &QTcpServer::newConnection, this, &WebListener::newConnection); } void WebListener::handleRequest(HttpClientConnection *connection, const HttpRequest &request) @@ -121,9 +122,16 @@ void WebListener::acceptError(QAbstractSocket::SocketError socketError) void WebListener::newConnection() { - auto connection = m_tcpServer->nextPendingConnection(); - if(!connection) + auto socket = m_server->nextPendingConnection(); + if(!socket) + { + qWarning() << "null socket received"; return; + } - new HttpClientConnection(*connection, *this); + auto client = new HttpClientConnection(*socket, *this); + m_clients.insert(client); + connect(client, &QObject::destroyed, this, [this, client](){ + m_clients.remove(client); + }); } diff --git a/webserverlib/weblistener.h b/webserverlib/weblistener.h index 8d9c4de..21944e7 100644 --- a/webserverlib/weblistener.h +++ b/webserverlib/weblistener.h @@ -6,10 +6,12 @@ #include #include #include +#include class QJsonObject; class QTcpServer; +class WebServer; class WebApplication; class HttpClientConnection; class HttpRequest; @@ -19,7 +21,7 @@ class WEBSERVERLIB_EXPORT WebListener : public QObject Q_OBJECT public: - WebListener(const QJsonObject &config, const QHash &applications, QObject *parent = Q_NULLPTR); + WebListener(const QJsonObject &config, WebServer &webServer); void start(); void handleRequest(HttpClientConnection *connection, const HttpRequest &request); @@ -29,10 +31,11 @@ private Q_SLOTS: void newConnection(); private: - using HostsContainer = QHash; + WebServer &m_webServer; - QTcpServer *m_tcpServer; - QHostAddress m_address; + QHostAddress m_hostAddress; quint16 m_port; - HostsContainer m_hosts; + QTcpServer *m_server; + QHash m_hosts; + QSet m_clients; }; diff --git a/webserverlib/webplugin.h b/webserverlib/webplugin.h index 958e3ef..5461fab 100644 --- a/webserverlib/webplugin.h +++ b/webserverlib/webplugin.h @@ -6,6 +6,7 @@ class QJsonObject; class WebApplication; +class WebServer; class WEBSERVERLIB_EXPORT WebPlugin : public QObject { @@ -15,7 +16,7 @@ public: WebPlugin(QObject *parent = Q_NULLPTR); virtual QString pluginName() const = 0; - virtual WebApplication *createApplication(const QJsonObject &config) const = 0; + virtual WebApplication *createApplication(const QJsonObject &config, WebServer &webServer) const = 0; }; Q_DECLARE_INTERFACE(WebPlugin, "dbsoftware.webserver.plugin/1.0") diff --git a/webserverlib/webserver.cpp b/webserverlib/webserver.cpp new file mode 100644 index 0000000..970c916 --- /dev/null +++ b/webserverlib/webserver.cpp @@ -0,0 +1,127 @@ +#include "webserver.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "webplugin.h" +#include "webapplication.h" +#include "weblistener.h" + +WebServer::WebServer(const QJsonObject &config, QObject *parent) : + QObject(parent) +{ + { + QDir dir(QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(QStringLiteral("plugins/") + QCoreApplication::applicationName())); + for(const auto &fileInfo : dir.entryInfoList(QDir::Files | QDir::NoSymLinks)) + { + if(!QLibrary::isLibrary(fileInfo.filePath())) + { + qWarning() << "skipping" << fileInfo.fileName() << "because no QLibrary"; + continue; // to skip windows junk files + } + + QPluginLoader pluginLoader(fileInfo.filePath()); + if(!pluginLoader.load()) + { + qCritical() << "error loading plugin" << fileInfo.fileName() << "because" << pluginLoader.errorString(); + continue; + } + + if(auto plugin = qobject_cast(pluginLoader.instance())) + { + const auto pluginName = plugin->pluginName(); + if(m_plugins.contains(pluginName)) + throw std::runtime_error(QString("duplicate plugin %0").arg(pluginName).toStdString()); + m_plugins.insert(pluginName, plugin); + } + else + qCritical() << "plugin" << fileInfo.fileName() << "could not be casted to WebPlugin"; + } + } + + if(!config.contains(QStringLiteral("applications"))) + throw std::runtime_error("settings does not contain a applications"); + + { + const auto applicationsVal = config.value(QStringLiteral("applications")); + if(!applicationsVal.isObject()) + throw std::runtime_error("applications is not a json object"); + + const auto applicationsList = applicationsVal.toObject(); + for(auto iter = applicationsList.constBegin(); iter != applicationsList.constEnd(); iter++) + { + const auto applicationVal = iter.value(); + if(!applicationVal.isObject()) + throw std::runtime_error(QString("application %0 is not a json object").arg(iter.key()).toStdString()); + + auto application = applicationVal.toObject(); + if(!application.contains(QStringLiteral("_pluginName"))) + throw std::runtime_error(QString("application %0 does not contain a _pluginName").arg(iter.key()).toStdString()); + + const auto pluginNameVal = application.take(QStringLiteral("_pluginName")); + if(!pluginNameVal.isString()) + throw std::runtime_error(QString("application %0 pluginName is not a string").arg(iter.key()).toStdString()); + + auto pluginName = pluginNameVal.toString(); + + const auto pluginsIter = m_plugins.find(pluginName); + if(pluginsIter == m_plugins.constEnd()) + throw std::runtime_error(QString("application %0 references not installed plugin %1").arg(iter.key(), pluginName).toStdString()); + + m_applications.insert(iter.key(), pluginsIter.value()->createApplication(application, *this)); + } + } + + if(!config.contains(QStringLiteral("listeners"))) + throw std::runtime_error("settings does not contain a listeners"); + + { + const auto listenersVal = config.value(QStringLiteral("listeners")); + if(!listenersVal.isArray()) + throw std::runtime_error("listeners is not a json array"); + + const auto listenersList = listenersVal.toArray(); + for(auto iter = listenersList.constBegin(); iter != listenersList.constEnd(); iter++) + { + const auto listenerVal = *iter; + if(!listenerVal.isObject()) + throw std::runtime_error(QString("listener %0 is not an object").arg(std::distance(listenersList.constBegin(), iter)).toStdString()); + + const auto listener = listenerVal.toObject(); + + m_listeners.insert(new WebListener(listener, *this)); + } + } +} + +void WebServer::start() +{ + for(auto iter = m_applications.constBegin(); iter != m_applications.constEnd(); iter++) + iter.value()->start(); + + for(auto listener : m_listeners) + listener->start(); +} + +const QHash &WebServer::plugins() const +{ + return m_plugins; +} + +const QHash &WebServer::applications() const +{ + return m_applications; +} + +const QSet &WebServer::listeners() const +{ + return m_listeners; +} diff --git a/webserverlib/webserver.h b/webserverlib/webserver.h new file mode 100644 index 0000000..a8be055 --- /dev/null +++ b/webserverlib/webserver.h @@ -0,0 +1,30 @@ +#pragma once + +#include "webserverlib_global.h" +#include + +#include +#include + +class QJsonObject; + +class WebPlugin; +class WebApplication; +class WebListener; + +class WEBSERVERLIB_EXPORT WebServer : public QObject +{ +public: + explicit WebServer(const QJsonObject &config, QObject *parent = Q_NULLPTR); + + void start(); + + const QHash &plugins() const; + const QHash &applications() const; + const QSet &listeners() const; + +private: + QHash m_plugins; + QHash m_applications; + QSet m_listeners; +}; diff --git a/webserverlib/webserverlib.pro b/webserverlib/webserverlib.pro index 0b0ab3f..2d42e76 100644 --- a/webserverlib/webserverlib.pro +++ b/webserverlib/webserverlib.pro @@ -13,7 +13,8 @@ SOURCES += \ webplugin.cpp \ httpclientconnection.cpp \ httprequest.cpp \ - httpresponse.cpp + httpresponse.cpp \ + webserver.cpp HEADERS += webserverlib_global.h \ weblistener.h \ @@ -21,7 +22,8 @@ HEADERS += webserverlib_global.h \ webplugin.h \ httpclientconnection.h \ httprequest.h \ - httpresponse.h + httpresponse.h \ + webserver.h FORMS +=