General improvement of memory handling and parent ownership of webserver classes
This commit is contained in:
@@ -1,121 +1,21 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QLibrary>
|
||||
#include <QPluginLoader>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QDebug>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <iterator>
|
||||
|
||||
#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<QString, WebPlugin*> 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<WebPlugin*>(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<QString, WebApplication*> applications;
|
||||
|
||||
const auto configPath = QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(QCoreApplication::applicationName() + QStringLiteral(".json"));
|
||||
const auto config = getJson<QJsonObject>(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<WebListener*> 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();
|
||||
}
|
||||
|
@@ -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"
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -8,11 +8,13 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include "utils/netutils.h"
|
||||
|
||||
#include "webserver.h"
|
||||
#include "httpclientconnection.h"
|
||||
#include "webapplication.h"
|
||||
|
||||
WebListener::WebListener(const QJsonObject &config, const QHash<QString, WebApplication*> &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 QHash<QString, WebAppl
|
||||
if(!hostAddressVal.isString())
|
||||
throw std::runtime_error("listener hostAddress is not a string");
|
||||
|
||||
m_address = parseHostAddress(hostAddressVal.toString());
|
||||
m_hostAddress = parseHostAddress(hostAddressVal.toString());
|
||||
|
||||
if(!config.contains(QStringLiteral("port")))
|
||||
throw std::runtime_error("listener does not contain port");
|
||||
@@ -32,7 +34,7 @@ WebListener::WebListener(const QJsonObject &config, const QHash<QString, WebAppl
|
||||
|
||||
m_port = portVal.toInt();
|
||||
|
||||
m_tcpServer = new QTcpServer(this);
|
||||
m_server = new QTcpServer(this);
|
||||
|
||||
if(!config.contains(QStringLiteral("vhosts")))
|
||||
throw std::runtime_error("listener does not contain vhosts");
|
||||
@@ -47,14 +49,14 @@ WebListener::WebListener(const QJsonObject &config, const QHash<QString, WebAppl
|
||||
const auto applicationNameVal = iter.value();
|
||||
if(!applicationNameVal.isString())
|
||||
throw std::runtime_error(QString("listener %0:%1 vhost %2 is not a string")
|
||||
.arg(m_address.toString()).arg(m_port).arg(iter.key()).toStdString());
|
||||
.arg(m_hostAddress.toString()).arg(m_port).arg(iter.key()).toStdString());
|
||||
|
||||
const auto applicationName = applicationNameVal.toString();
|
||||
|
||||
const auto applicationsIter = applications.find(applicationName);
|
||||
if(applicationsIter == applications.constEnd())
|
||||
const auto applicationsIter = m_webServer.applications().find(applicationName);
|
||||
if(applicationsIter == m_webServer.applications().constEnd())
|
||||
throw std::runtime_error(QString("listener %0:%1 vhost %2 references unknown application %3")
|
||||
.arg(m_address.toString()).arg(m_port).arg(iter.key(), applicationName).toStdString());
|
||||
.arg(m_hostAddress.toString()).arg(m_port).arg(iter.key(), applicationName).toStdString());
|
||||
|
||||
m_hosts.insert(iter.key(), *applicationsIter);
|
||||
}
|
||||
@@ -62,13 +64,12 @@ WebListener::WebListener(const QJsonObject &config, const QHash<QString, WebAppl
|
||||
|
||||
void WebListener::start()
|
||||
{
|
||||
qDebug() << "starting listening" << m_address << m_port;
|
||||
if(!m_tcpServer->listen(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);
|
||||
});
|
||||
}
|
||||
|
@@ -6,10 +6,12 @@
|
||||
#include <QAbstractSocket>
|
||||
#include <QHostAddress>
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
|
||||
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<QString, WebApplication*> &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<QString, WebApplication*>;
|
||||
WebServer &m_webServer;
|
||||
|
||||
QTcpServer *m_tcpServer;
|
||||
QHostAddress m_address;
|
||||
QHostAddress m_hostAddress;
|
||||
quint16 m_port;
|
||||
HostsContainer m_hosts;
|
||||
QTcpServer *m_server;
|
||||
QHash<QString, WebApplication*> m_hosts;
|
||||
QSet<HttpClientConnection*> m_clients;
|
||||
};
|
||||
|
@@ -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")
|
||||
|
127
webserverlib/webserver.cpp
Normal file
127
webserverlib/webserver.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#include "webserver.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QCoreApplication>
|
||||
#include <QLibrary>
|
||||
#include <QPluginLoader>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#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<WebPlugin*>(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<QString, WebPlugin *> &WebServer::plugins() const
|
||||
{
|
||||
return m_plugins;
|
||||
}
|
||||
|
||||
const QHash<QString, WebApplication *> &WebServer::applications() const
|
||||
{
|
||||
return m_applications;
|
||||
}
|
||||
|
||||
const QSet<WebListener *> &WebServer::listeners() const
|
||||
{
|
||||
return m_listeners;
|
||||
}
|
30
webserverlib/webserver.h
Normal file
30
webserverlib/webserver.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserverlib_global.h"
|
||||
#include <QObject>
|
||||
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
|
||||
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<QString, WebPlugin*> &plugins() const;
|
||||
const QHash<QString, WebApplication*> &applications() const;
|
||||
const QSet<WebListener*> &listeners() const;
|
||||
|
||||
private:
|
||||
QHash<QString, WebPlugin*> m_plugins;
|
||||
QHash<QString, WebApplication*> m_applications;
|
||||
QSet<WebListener*> m_listeners;
|
||||
};
|
@@ -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 +=
|
||||
|
||||
|
Reference in New Issue
Block a user