Add debug server interface

This commit is contained in:
Simon Stürz 2018-02-09 12:51:20 +01:00 committed by Michael Zanetti
parent aede5f2afa
commit 5e69772035
17 changed files with 1009 additions and 21 deletions

7
debian/changelog vendored
View File

@ -1,3 +1,10 @@
guh (0.8.3) xenial; urgency=medium
* Add debug server
* Add interfaces
-- Simon Stürz <simon.stuerz@guh.io> Thu, 08 Feb 2018 23:10:42 +0100
guh (0.8.2) xenial; urgency=medium
* New hardware manager api

View File

@ -6,7 +6,7 @@ GUH_PLUGINS_PATH=/usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH')/gu
# define protocol versions
JSON_PROTOCOL_VERSION_MAJOR=1
JSON_PROTOCOL_VERSION_MINOR=1
JSON_PROTOCOL_VERSION_MINOR=2
REST_API_VERSION=1
DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" \

View File

@ -0,0 +1,560 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2018 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh 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 guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "guhcore.h"
#include "httpreply.h"
#include "guhsettings.h"
#include "httprequest.h"
#include "loggingcategories.h"
#include "debugserverhandler.h"
#include <QXmlStreamWriter>
#include <QCoreApplication>
namespace guhserver {
DebugServerHandler::DebugServerHandler(QObject *parent) : QObject(parent)
{
}
QByteArray DebugServerHandler::createDebugXmlDocument()
{
QByteArray data;
QXmlStreamWriter writer(&data);
writer.setAutoFormatting(true);
writer.writeStartDocument("1.0");
writer.writeProcessingInstruction("DOCUMENT", "html");
writer.writeComment("Auto generated html page from nymea");
writer.writeStartElement("html");
writer.writeAttribute("lang", GuhCore::instance()->configuration()->locale().name());
writer.writeStartElement("head");
writer.writeEmptyElement("meta");
writer.writeAttribute("http-equiv", "Content-Type");
writer.writeAttribute("content", "text/html; charset=utf-8");
writer.writeTextElement("title", QCoreApplication::translate("main", "Debug nymea"));
writer.writeEndElement(); // head
writer.writeStartElement("body");
// Welcome section
writer.writeTextElement("h1", QCoreApplication::translate("main", "nymea debug interface"));
writer.writeEmptyElement("hr");
writer.writeTextElement("p", QCoreApplication::translate("main", "Welcome to the debug interface. This debug interface was designed to provide an easy possibility to get helpful information about the running nymea server. This interfaces provides read only options and is disabled by default."));
writer.writeEmptyElement("hr");
writer.writeTextElement("h3", QCoreApplication::translate("main", "Warning"));
writer.writeTextElement("p", QCoreApplication::translate("main", "Be aware that this debug interface is a security breach and offers access to the system log and therefore to possibly sensible data. If you are not using the debug tools, than you should disable this debug server."));
writer.writeTextElement("p", QCoreApplication::translate("main", "The debug server can be disabled by changing the the value \"debugServerEnabled=false\" in the \"guhd\" section of %1 or using the API method \"Configuration.SetDebugServerEnabled\".").arg(GuhSettings(GuhSettings::SettingsRoleGlobal).fileName()));
writer.writeEmptyElement("hr");
// System information section
writer.writeTextElement("h2", QCoreApplication::translate("main", "Server information"));
writer.writeEmptyElement("hr");
writer.writeStartElement("table");
writer.writeAttribute("width", "100%");
writer.writeAttribute("border", "1");
writer.writeStartElement("col");
writer.writeAttribute("align", "left");
writer.writeEndElement(); // col
writer.writeStartElement("col");
writer.writeAttribute("align", "right");
writer.writeEndElement(); // col
QString userName = qgetenv("USER");
if (userName.isEmpty())
userName = qgetenv("USERNAME");
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "User"));
writer.writeTextElement("td", userName);
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Compiled with Qt version"));
writer.writeTextElement("td", QT_VERSION_STR);
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Qt runtime version"));
writer.writeTextElement("td", qVersion());
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Command"));
writer.writeTextElement("td", QCoreApplication::arguments().join(' '));
writer.writeEndElement(); // tr
if (!qgetenv("SNAP").isEmpty()) {
// Note: http://snapcraft.io/docs/reference/env
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Snap name"));
writer.writeTextElement("td", qgetenv("SNAP_NAME"));
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Snap version"));
writer.writeTextElement("td", qgetenv("SNAP_VERSION"));
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Snap directory"));
writer.writeTextElement("td", qgetenv("SNAP"));
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Snap application data"));
writer.writeTextElement("td", qgetenv("SNAP_DATA"));
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Snap user data"));
writer.writeTextElement("td", qgetenv("SNAP_USER_DATA"));
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Snap common data"));
writer.writeTextElement("td", qgetenv("SNAP_COMMON"));
writer.writeEndElement(); // tr
}
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Server name"));
writer.writeTextElement("td", GuhCore::instance()->configuration()->serverName());
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Server version"));
writer.writeTextElement("td", GUH_VERSION_STRING);
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "JSON-RPC version"));
writer.writeTextElement("td", JSON_PROTOCOL_VERSION);
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Language"));
writer.writeTextElement("td", GuhCore::instance()->configuration()->locale().name() + " (" + GuhCore::instance()->configuration()->locale().nativeCountryName() + " - " + GuhCore::instance()->configuration()->locale().nativeLanguageName() + ")");
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Timezone"));
writer.writeTextElement("td", QString::fromUtf8(GuhCore::instance()->configuration()->timeZone()));
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Server UUID"));
writer.writeTextElement("td", GuhCore::instance()->configuration()->serverUuid().toString());
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Settings path"));
writer.writeTextElement("td", GuhSettings(GuhSettings::SettingsRoleGlobal).settingsPath());
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Translations path"));
writer.writeTextElement("td", GuhSettings(GuhSettings::SettingsRoleGlobal).translationsPath());
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Log database"));
writer.writeTextElement("td", GuhSettings(GuhSettings::SettingsRoleGlobal).logPath());
writer.writeEndElement(); // tr
for (int i = 0; i < GuhCore::instance()->deviceManager()->pluginSearchDirs().count(); i++) {
writer.writeStartElement("tr");
writer.writeEndElement(); // tr
if (i == 0) {
writer.writeTextElement("th", QCoreApplication::translate("main", "Plugin paths"));
} else {
writer.writeTextElement("th", "");
}
writer.writeTextElement("td", GuhCore::instance()->deviceManager()->pluginSearchDirs().at(i));
}
writer.writeEndElement(); // table
// Downloads section
writer.writeEmptyElement("hr");
writer.writeTextElement("h2", QCoreApplication::translate("main", "Downloads"));
writer.writeEmptyElement("hr");
// Logs download section
writer.writeTextElement("h3", QCoreApplication::translate("main", "Logs"));
writer.writeStartElement("table");
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Log database"));
writer.writeStartElement("td");
writer.writeStartElement("form");
writer.writeAttribute("method", "get");
writer.writeAttribute("action", "/debug/logdb.sql");
writer.writeStartElement("button");
writer.writeAttribute("type", "submit");
writer.writeCharacters(QCoreApplication::translate("main", "Download"));
writer.writeEndElement(); // button
writer.writeEndElement(); // form
writer.writeEndElement(); // td
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Syslog"));
writer.writeStartElement("td");
writer.writeStartElement("form");
writer.writeAttribute("method", "get");
writer.writeAttribute("action", "/debug/syslog");
writer.writeStartElement("button");
writer.writeAttribute("type", "submit");
writer.writeCharacters(QCoreApplication::translate("main", "Download"));
writer.writeEndElement(); // button
writer.writeEndElement(); // form
writer.writeEndElement(); // td
writer.writeEndElement(); // tr
// TODO: offer logfile download if specified in the command line options
writer.writeEndElement(); // table
// Settings download section
writer.writeTextElement("h3", QCoreApplication::translate("main", "Settings"));
writer.writeStartElement("table");
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Guhd settings"));
writer.writeStartElement("td");
writer.writeStartElement("form");
writer.writeAttribute("method", "get");
writer.writeAttribute("action", "/debug/settings/guhd");
writer.writeStartElement("button");
writer.writeAttribute("type", "submit");
writer.writeCharacters(QCoreApplication::translate("main", "Download"));
writer.writeEndElement(); // button
writer.writeEndElement(); // form
writer.writeEndElement(); // td
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Device settings"));
writer.writeStartElement("td");
writer.writeStartElement("form");
writer.writeAttribute("method", "get");
writer.writeAttribute("action", "/debug/settings/devices");
writer.writeStartElement("button");
writer.writeAttribute("type", "submit");
writer.writeCharacters(QCoreApplication::translate("main", "Download"));
writer.writeEndElement(); // button
writer.writeEndElement(); // form
writer.writeEndElement(); // td
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Device state settings"));
writer.writeStartElement("td");
writer.writeStartElement("form");
writer.writeAttribute("method", "get");
writer.writeAttribute("action", "/debug/settings/devicestates");
writer.writeStartElement("button");
writer.writeAttribute("type", "submit");
writer.writeCharacters(QCoreApplication::translate("main", "Download"));
writer.writeEndElement(); // button
writer.writeEndElement(); // form
writer.writeEndElement(); // td
writer.writeEndElement(); // tr
writer.writeStartElement("tr");
writer.writeTextElement("th", QCoreApplication::translate("main", "Rules settings"));
writer.writeStartElement("td");
writer.writeStartElement("form");
writer.writeAttribute("method", "get");
writer.writeAttribute("action", "/debug/settings/rules");
writer.writeStartElement("button");
writer.writeAttribute("type", "submit");
writer.writeCharacters(QCoreApplication::translate("main", "Download"));
writer.writeEndElement(); // button
writer.writeEndElement(); // form
writer.writeEndElement(); // td
writer.writeEndElement(); // tr
writer.writeEndElement(); // table
writer.writeEmptyElement("hr");
writer.writeStartElement("footer");
writer.writeTextElement("p", QString("Copyright %1 2018 guh GmbH.").arg(QChar(0xA9)));
writer.writeTextElement("p", QCoreApplication::translate("main", "Released under the GNU GENERAL PUBLIC LICENSE Version 2."));
writer.writeEndElement(); // footer
writer.writeEndElement(); // body
writer.writeEndElement(); // html
return data;
}
QByteArray DebugServerHandler::createErrorXmlDocument(HttpReply::HttpStatusCode statusCode, const QString &errorMessage)
{
QByteArray data;
QXmlStreamWriter writer(&data);
writer.setAutoFormatting(true);
writer.writeStartDocument("1.0");
writer.writeComment("Live generated html page from nymea");
writer.writeStartElement("html");
writer.writeAttribute("lang", GuhCore::instance()->configuration()->locale().name());
writer.writeStartElement("head");
writer.writeEmptyElement("meta");
writer.writeAttribute("http-equiv", "Content-Type");
writer.writeAttribute("content", "text/html; charset=utf-8");
writer.writeTextElement("title", QCoreApplication::translate("main", "Debug nymea"));
writer.writeEndElement(); // head
writer.writeStartElement("body");
writer.writeTextElement("h1", QCoreApplication::translate("main", "Error") + QString(" %1").arg(static_cast<int>(statusCode)));
writer.writeEmptyElement("hr");
writer.writeTextElement("p", errorMessage);
writer.writeEmptyElement("hr");
writer.writeStartElement("footer");
writer.writeTextElement("p", QString("Copyright %1 2018 guh GmbH.").arg(QChar(0xA9)));
writer.writeTextElement("p", QCoreApplication::translate("main", "Released under the GNU GENERAL PUBLIC LICENSE Version 2."));
writer.writeEndElement(); // footer
writer.writeEndElement(); // body
writer.writeEndElement(); // html
return data;
}
HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
{
qCDebug(dcWebServer()) << "Debug request for" << requestPath;
// Check if this is a logdb requested
if (requestPath.startsWith("/debug/logdb.sql")) {
qCDebug(dcWebServer()) << "Loading" << GuhSettings::logPath();
QFile logDatabaseFile(GuhSettings::logPath());
if (!logDatabaseFile.exists()) {
qCWarning(dcWebServer()) << "Could not read log database file for debug download" << GuhSettings::logPath() << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, QCoreApplication::translate("main", "Could not find file") + " " + logDatabaseFile.fileName()));
return reply;
}
if (!logDatabaseFile.open(QFile::ReadOnly)) {
qCWarning(dcWebServer()) << "Could not read log database file for debug download" << GuhSettings::logPath();
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, QCoreApplication::translate("main", "Could not open file") + " " + logDatabaseFile.fileName()));
return reply;
}
QByteArray logDatabaseRawData = logDatabaseFile.readAll();
logDatabaseFile.close();
HttpReply *reply = RestResource::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/sql");
reply->setPayload(logDatabaseRawData);
return reply;
}
// Check if this is a syslog requested
if (requestPath.startsWith("/debug/syslog")) {
QString syslogFileName = "/var/log/syslog";
qCDebug(dcWebServer()) << "Loading" << syslogFileName;
QFile syslogFile(syslogFileName);
if (!syslogFile.exists()) {
qCWarning(dcWebServer()) << "Could not read log database file for debug download" << syslogFileName << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, QCoreApplication::translate("main", "Could not find file") + " " + syslogFileName));
return reply;
}
if (!syslogFile.open(QFile::ReadOnly)) {
qCWarning(dcWebServer()) << "Could not read syslog file for debug download" << syslogFileName;
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, QCoreApplication::translate("main", "Could not open file") + " " + syslogFileName));
return reply;
}
QByteArray syslogFileData = syslogFile.readAll();
syslogFile.close();
HttpReply *reply = RestResource::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
reply->setPayload(syslogFileData);
return reply;
}
// Check if this is a settings request
if (requestPath.startsWith("/debug/settings")) {
if (requestPath.startsWith("/debug/settings/devices")) {
QString settingsFileName = GuhSettings(GuhSettings::SettingsRoleDevices).fileName();
qCDebug(dcWebServer()) << "Loading" << settingsFileName;
QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) {
qCWarning(dcWebServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, QCoreApplication::translate("main", "Could not find file") + " " + settingsFileName));
return reply;
}
if (!settingsFile.open(QFile::ReadOnly)) {
qCWarning(dcWebServer()) << "Could not read file for debug download" << settingsFileName;
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, QCoreApplication::translate("main", "Could not open file") + " " + settingsFileName));
return reply;
}
QByteArray settingsFileData = settingsFile.readAll();
settingsFile.close();
HttpReply *reply = RestResource::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
reply->setPayload(settingsFileData);
return reply;
}
if (requestPath.startsWith("/debug/settings/rules")) {
QString settingsFileName = GuhSettings(GuhSettings::SettingsRoleRules).fileName();
qCDebug(dcWebServer()) << "Loading" << settingsFileName;
QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) {
qCWarning(dcWebServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, QCoreApplication::translate("main", "Could not find file") + " " + settingsFileName));
return reply;
}
if (!settingsFile.open(QFile::ReadOnly)) {
qCWarning(dcWebServer()) << "Could not read file for debug download" << settingsFileName;
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, QCoreApplication::translate("main", "Could not open file") + " " + settingsFileName));
return reply;
}
QByteArray settingsFileData = settingsFile.readAll();
settingsFile.close();
HttpReply *reply = RestResource::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
reply->setPayload(settingsFileData);
return reply;
}
if (requestPath.startsWith("/debug/settings/guhd")) {
QString settingsFileName = GuhSettings(GuhSettings::SettingsRoleGlobal).fileName();
qCDebug(dcWebServer()) << "Loading" << settingsFileName;
QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) {
qCWarning(dcWebServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, QCoreApplication::translate("main", "Could not find file") + " " + settingsFileName));
return reply;
}
if (!settingsFile.open(QFile::ReadOnly)) {
qCWarning(dcWebServer()) << "Could not read file for debug download" << settingsFileName;
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, QCoreApplication::translate("main", "Could not open file") + " " + settingsFileName));
return reply;
}
QByteArray settingsFileData = settingsFile.readAll();
settingsFile.close();
HttpReply *reply = RestResource::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
reply->setPayload(settingsFileData);
return reply;
}
if (requestPath.startsWith("/debug/settings/devicestates")) {
QString settingsFileName = GuhSettings(GuhSettings::SettingsRoleDeviceStates).fileName();
qCDebug(dcWebServer()) << "Loading" << settingsFileName;
QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) {
qCWarning(dcWebServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, QCoreApplication::translate("main", "Could not find file") + " " + settingsFileName));
return reply;
}
if (!settingsFile.open(QFile::ReadOnly)) {
qCWarning(dcWebServer()) << "Could not read file for debug download" << settingsFileName;
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, QCoreApplication::translate("main", "Could not open file") + " " + settingsFileName));
return reply;
}
QByteArray settingsFileData = settingsFile.readAll();
settingsFile.close();
HttpReply *reply = RestResource::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
reply->setPayload(settingsFileData);
return reply;
}
}
qCDebug(dcWebServer()) << "Create debug interface page";
// Fallback default debug page
HttpReply *reply = RestResource::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createDebugXmlDocument());
return reply;
}
}

View File

@ -0,0 +1,46 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2018 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh 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 guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DEBUGSERVERHANDLER_H
#define DEBUGSERVERHANDLER_H
#include <QObject>
#include "httpreply.h"
namespace guhserver {
class DebugServerHandler : public QObject
{
Q_OBJECT
public:
explicit DebugServerHandler(QObject *parent = nullptr);
HttpReply *processDebugRequest(const QString &requestPath);
private:
QByteArray createDebugXmlDocument();
QByteArray createErrorXmlDocument(HttpReply::HttpStatusCode statusCode, const QString &errorMessage);
};
}
#endif // DEBUGSERVERHANDLER_H

View File

@ -56,6 +56,7 @@ GuhConfiguration::GuhConfiguration(QObject *parent) :
setLocale(locale());
setBluetoothServerEnabled(bluetoothServerEnabled());
setSslCertificate(sslCertificate(), sslCertificateKey());
setDebugServerEnabled(debugServerEnabled());
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
@ -387,6 +388,25 @@ void GuhConfiguration::setSslCertificate(const QString &sslCertificate, const QS
settings.endGroup();
}
bool GuhConfiguration::debugServerEnabled() const
{
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
settings.beginGroup("guhd");
return settings.value("debugServerEnabled", false).toBool();
}
void GuhConfiguration::setDebugServerEnabled(bool enabled)
{
qCDebug(dcApplication()) << "Configuration: Set debug server" << (enabled ? "enabled" : "disabled");
if (debugServerEnabled() != enabled) {
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
settings.beginGroup("guhd");
settings.setValue("debugServerEnabled", enabled);
settings.endGroup();
emit debugServerEnabledChanged(enabled);
}
}
void GuhConfiguration::setServerUuid(const QUuid &uuid)
{
qCDebug(dcApplication()) << "Configuration: Server uuid:" << uuid.toString();

View File

@ -90,6 +90,10 @@ public:
QString sslCertificateKey() const;
void setSslCertificate(const QString &sslCertificate, const QString &sslCertificateKey);
// Debug server
bool debugServerEnabled() const;
void setDebugServerEnabled(bool enabled);
// TCP server
QHash<QString, ServerConfiguration> tcpServerConfigurations() const;
void setTcpServerConfiguration(const ServerConfiguration &config);
@ -148,6 +152,7 @@ signals:
void bluetoothServerEnabledChanged();
void cloudEnabledChanged(bool enabled);
void debugServerEnabledChanged(bool enabled);
};
}

View File

@ -461,6 +461,11 @@ CloudManager *GuhCore::cloudManager() const
return m_cloudManager;
}
DebugServerHandler *GuhCore::debugServerHandler() const
{
return m_debugServerHandler;
}
/*! Constructs GuhCore with the given \a parent. This is private.
Use \l{GuhCore::instance()} to access the single instance.*/
@ -495,10 +500,12 @@ void GuhCore::init() {
qCDebug(dcApplication) << "Creating Server Manager";
m_serverManager = new ServerManager(m_configuration, this);
// Create the NetworkManager
qCDebug(dcApplication) << "Creating Network Manager";
m_networkManager = new NetworkManager(this);
qCDebug(dcApplication) << "Creating Debug Server Handler";
m_debugServerHandler = new DebugServerHandler(this);
qCDebug(dcApplication) << "Creating Cloud Manager";
m_cloudManager = new CloudManager(m_networkManager, this);
m_cloudManager->setDeviceId(m_configuration->serverUuid());

View File

@ -38,6 +38,8 @@
#include "time/timemanager.h"
#include "hardwaremanagerimplementation.h"
#include "debugserverhandler.h"
#include <QObject>
class Device;
@ -80,6 +82,7 @@ public:
NetworkManager *networkManager() const;
UserManager *userManager() const;
CloudManager *cloudManager() const;
DebugServerHandler *debugServerHandler() const;
static QStringList getAvailableLanguages();
@ -115,6 +118,7 @@ private:
TimeManager *m_timeManager;
CloudManager *m_cloudManager;
HardwareManagerImplementation *m_hardwareManager;
DebugServerHandler *m_debugServerHandler;
NetworkManager *m_networkManager;
UserManager *m_userManager;

View File

@ -89,6 +89,7 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
basicConfiguration.insert("serverTime", JsonTypes::basicTypeToString(JsonTypes::Uint));
basicConfiguration.insert("timeZone", JsonTypes::basicTypeToString(JsonTypes::String));
basicConfiguration.insert("language", JsonTypes::basicTypeToString(JsonTypes::String));
basicConfiguration.insert("debugServerEnabled", JsonTypes::basicTypeToString(JsonTypes::Bool));
returns.insert("basicConfiguration", basicConfiguration);
QVariantList tcpServerConfigurations;
tcpServerConfigurations.append(JsonTypes::serverConfigurationRef());
@ -125,6 +126,13 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
returns.insert("configurationError", JsonTypes::configurationErrorRef());
setReturns("SetLanguage", returns);
params.clear(); returns.clear();
setDescription("SetDebugServerEnabled", "Enable or disable the debug server.");
params.insert("enabled", JsonTypes::basicTypeToString(JsonTypes::String));
setParams("SetDebugServerEnabled", params);
returns.insert("configurationError", JsonTypes::configurationErrorRef());
setReturns("SetDebugServerEnabled", returns);
params.clear(); returns.clear();
setDescription("SetTcpServerConfiguration", "Configure a TCP interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added. Note: if you are changing the configuration for the interface you are currently connected to, the connection will be dropped.");
params.insert("configuration", JsonTypes::serverConfigurationRef());
@ -181,6 +189,7 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
params.insert("serverUuid", JsonTypes::basicTypeToString(JsonTypes::Uuid));
params.insert("serverTime", JsonTypes::basicTypeToString(JsonTypes::Uint));
params.insert("timeZone", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("debugServerEnabled", JsonTypes::basicTypeToString(JsonTypes::Bool));
setParams("BasicConfigurationChanged", params);
params.clear(); returns.clear();
@ -214,6 +223,7 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
connect(GuhCore::instance()->configuration(), &GuhConfiguration::serverNameChanged, this, &ConfigurationHandler::onBasicConfigurationChanged);
connect(GuhCore::instance()->configuration(), &GuhConfiguration::timeZoneChanged, this, &ConfigurationHandler::onBasicConfigurationChanged);
connect(GuhCore::instance()->configuration(), &GuhConfiguration::localeChanged, this, &ConfigurationHandler::onBasicConfigurationChanged);
connect(GuhCore::instance()->configuration(), &GuhConfiguration::debugServerEnabledChanged, this, &ConfigurationHandler::onBasicConfigurationChanged);
connect(GuhCore::instance()->configuration(), &GuhConfiguration::tcpServerConfigurationChanged, this, &ConfigurationHandler::onTcpServerConfigurationChanged);
connect(GuhCore::instance()->configuration(), &GuhConfiguration::webServerConfigurationChanged, this, &ConfigurationHandler::onWebServerConfigurationChanged);
connect(GuhCore::instance()->configuration(), &GuhConfiguration::webSocketServerConfigurationChanged, this, &ConfigurationHandler::onWebSocketServerConfigurationChanged);
@ -416,6 +426,13 @@ JsonReply *ConfigurationHandler::SetCloudEnabled(const QVariantMap &params) cons
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorNoError));
}
JsonReply *ConfigurationHandler::SetDebugServerEnabled(const QVariantMap &params) const
{
bool enabled = params.value("enabled").toBool();
GuhCore::instance()->configuration()->setDebugServerEnabled(enabled);
return createReply(statusToReply(GuhConfiguration::ConfigurationErrorNoError));
}
void ConfigurationHandler::onBasicConfigurationChanged()
{
QVariantMap params;

View File

@ -48,6 +48,7 @@ public:
Q_INVOKABLE JsonReply *SetWebSocketServerConfiguration(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *DeleteWebSocketServerConfiguration(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *SetCloudEnabled(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *SetDebugServerEnabled(const QVariantMap &params) const;
signals:
void BasicConfigurationChanged(const QVariantMap &params);

View File

@ -1119,6 +1119,7 @@ QVariantMap JsonTypes::packBasicConfiguration()
basicConfiguration.insert("serverTime", GuhCore::instance()->timeManager()->currentDateTime().toTime_t());
basicConfiguration.insert("timeZone", QString::fromUtf8(GuhCore::instance()->timeManager()->timeZone()));
basicConfiguration.insert("language", GuhCore::instance()->configuration()->locale().name());
basicConfiguration.insert("debugServerEnabled", GuhCore::instance()->configuration()->debugServerEnabled());
return basicConfiguration;
}

View File

@ -96,6 +96,7 @@ HEADERS += guhcore.h \
hardware/network/avahi/qtavahiservice_p.h \
hardware/network/avahi/qtavahiservicebrowserimplementation.h \
hardware/network/avahi/qtavahiservicebrowserimplementation_p.h \
debugserverhandler.h
SOURCES += guhcore.cpp \
tcpserver.cpp \
@ -174,3 +175,4 @@ SOURCES += guhcore.cpp \
hardware/network/avahi/qtavahiservice_p.cpp \
hardware/network/avahi/qtavahiservicebrowserimplementation.cpp \
hardware/network/avahi/qtavahiservicebrowserimplementation_p.cpp \
debugserverhandler.cpp

View File

@ -76,6 +76,7 @@
#include "httpreply.h"
#include "httprequest.h"
#include "rest/restresource.h"
#include "debugserverhandler.h"
#include <QJsonDocument>
#include <QNetworkInterface>
@ -281,14 +282,14 @@ void WebServer::readClient()
QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
QUuid clientId = m_clientList.key(socket);
// check client
// Check client
if (clientId.isNull()) {
qCWarning(dcWebServer) << "Client not recognized";
socket->close();
return;
}
// read HTTP request
// Read HTTP request
QByteArray data = socket->readAll();
HttpRequest request;
@ -300,13 +301,13 @@ void WebServer::readClient()
request = HttpRequest(data);
}
// check if the request is complete
// Check if the request is complete
if (!request.isComplete()) {
m_incompleteRequests.insert(socket, request);
return;
}
// check if the request is valid
// Check if the request is valid
if (!request.isValid()) {
qCWarning(dcWebServer) << "Got invalid request:" << request.url().path();
HttpReply *reply = RestResource::createErrorReply(HttpReply::BadRequest);
@ -316,7 +317,7 @@ void WebServer::readClient()
return;
}
// check HTTP version
// Check HTTP version
if (request.httpVersion() != "HTTP/1.1" && request.httpVersion() != "HTTP/1.0") {
qCWarning(dcWebServer) << "HTTP version is not supported." << request.httpVersion();
HttpReply *reply = RestResource::createErrorReply(HttpReply::HttpVersionNotSupported);
@ -329,7 +330,7 @@ void WebServer::readClient()
qCDebug(dcWebServer) << QString("Got valid request from %1:%2").arg(socket->peerAddress().toString()).arg(socket->peerPort());
qCDebug(dcWebServer) << request.methodString() << request.url().path();
// reset timout
// Reset timout
foreach (WebServerClient *webserverClient, m_webServerClients) {
if (webserverClient->address() == socket->peerAddress()) {
webserverClient->resetTimout(socket);
@ -337,7 +338,7 @@ void WebServer::readClient()
}
}
// verify method
// Verify method
if (request.method() == HttpRequest::Unhandled) {
HttpReply *reply = RestResource::createErrorReply(HttpReply::MethodNotAllowed);
reply->setClientId(clientId);
@ -347,13 +348,13 @@ void WebServer::readClient()
return;
}
// verify API query
// Verify API query
if (request.url().path().startsWith("/api/v1")) {
emit httpRequestReady(clientId, request);
return;
}
// check icon call
// Check icon call
if (request.url().path().startsWith("/icons/") && request.method() == HttpRequest::Get) {
HttpReply *reply = processIconRequest(request.url().path());
reply->setClientId(clientId);
@ -362,7 +363,37 @@ void WebServer::readClient()
return;
}
// check server.xml call
// Check if this is a debug call
if (request.url().path().startsWith("/debug")) {
// Check if debug server is enabled
if (GuhCore::instance()->configuration()->debugServerEnabled()) {
// Verify methods
if (request.method() != HttpRequest::Get && request.method() != HttpRequest::Options) {
HttpReply *reply = RestResource::createErrorReply(HttpReply::MethodNotAllowed);
reply->setClientId(clientId);
reply->setHeader(HttpReply::AllowHeader, "GET, OPTIONS");
sendHttpReply(reply);
reply->deleteLater();
return;
}
HttpReply *reply = GuhCore::instance()->debugServerHandler()->processDebugRequest(request.url().path());
reply->setClientId(clientId);
sendHttpReply(reply);
reply->deleteLater();
return;
} else {
qCWarning(dcWebServer()) << "The debug server handler is disabled. You can enable it by adding \'debugServerEnabled=true\' in the \'guhd\' section of the guhd.conf file.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setClientId(clientId);
sendHttpReply(reply);
reply->deleteLater();
return;
}
}
// Check server.xml call
if (request.url().path() == "/server.xml" && request.method() == HttpRequest::Get) {
qCDebug(dcWebServer) << "server XML request call";
HttpReply *reply = RestResource::createSuccessReply();
@ -375,9 +406,9 @@ void WebServer::readClient()
}
// request for a file...
// Request for a file...
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
if (!QDir(m_configuration.publicFolder).exists()) {
qCWarning(dcWebServer) << "webinterface folder" << m_configuration.publicFolder << "does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
@ -396,7 +427,7 @@ void WebServer::readClient()
qCDebug(dcWebServer) << "load file" << file.fileName();
HttpReply *reply = RestResource::createSuccessReply();
// check content type
// Check content type
if (file.fileName().endsWith(".html")) {
reply->setHeader(HttpReply::ContentTypeHeader, "text/html; charset=\"utf-8\";");
} else if (file.fileName().endsWith(".css")) {
@ -429,7 +460,7 @@ void WebServer::readClient()
}
}
// reject everything else...
// Reject everything else...
qCWarning(dcWebServer) << "Unknown message received.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotImplemented);
reply->setClientId(clientId);
@ -441,7 +472,7 @@ void WebServer::onDisconnected()
{
QSslSocket* socket = static_cast<QSslSocket *>(sender());
// remove connection from server client
// Remove connection from server client
foreach (WebServerClient *client, m_webServerClients) {
if (client->address() == socket->peerAddress()) {
client->removeConnection(socket);
@ -730,7 +761,6 @@ QByteArray WebServer::createServerXmlDocument(QHostAddress address)
return data;
}
/*!
\class guhserver::WebServerClient
\brief This class represents a client the web server for guhd.

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* Copyright (C) 2015-2018 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
@ -43,8 +43,8 @@
namespace guhserver {
class HttpRequest;
class HttpReply;
class HttpRequest;
class WebServerClient : public QObject
{
@ -98,6 +98,7 @@ private:
QByteArray createServerXmlDocument(QHostAddress address);
HttpReply *processIconRequest(const QString &fileName);
HttpReply *processDebugRequest(const QString &requestPath);
protected:
void incomingConnection(qintptr socketDescriptor) override;

View File

@ -1,4 +1,4 @@
1.1
1.2
{
"methods": {
"Actions.ExecuteAction": {
@ -75,6 +75,7 @@
},
"returns": {
"basicConfiguration": {
"debugServerEnabled": "Bool",
"language": "String",
"serverName": "String",
"serverTime": "Uint",
@ -114,6 +115,15 @@
"configurationError": "$ref:ConfigurationError"
}
},
"Configuration.SetDebugServerEnabled": {
"description": "Enable or disable the debug server.",
"params": {
"enabled": "String"
},
"returns": {
"configurationError": "$ref:ConfigurationError"
}
},
"Configuration.SetLanguage": {
"description": "Sets the server language to the given language. See also: \"GetAvailableLanguages\"",
"params": {
@ -785,6 +795,7 @@
"Configuration.BasicConfigurationChanged": {
"description": "Emitted whenever the basic configuration of this server changes.",
"params": {
"debugServerEnabled": "Bool",
"serverName": "String",
"serverTime": "Uint",
"serverUuid": "Uuid",

View File

@ -45,6 +45,8 @@ private slots:
void testServerName();
void testLanguages();
void testDebugServerConfiguration();
private:
QVariantMap loadBasicConfiguration();
@ -103,12 +105,16 @@ void TestConfigurations::testTimeZones()
configurationChangedNotifications = checkNotifications(notificationSpy, "Configuration.BasicConfigurationChanged");
QVERIFY2(configurationChangedNotifications.count() == 1, "Should get only one Configuration.BasicConfigurationChanged notification");
QVariantMap notificationContent = configurationChangedNotifications.first().toMap().value("params").toMap();
qDebug() << notificationContent;
QVERIFY2(notificationContent.contains("basicConfiguration"), "Notification does not contain basicConfiguration");
QVariantMap basicConfigurationNotificationMap = notificationContent.value("basicConfiguration").toMap();
QVERIFY2(basicConfigurationNotificationMap.contains("language"), "Notification does not contain key language");
QVERIFY2(basicConfigurationNotificationMap.contains("serverName"), "Notification does not contain key serverName");
QVERIFY2(basicConfigurationNotificationMap.contains("serverTime"), "Notification does not contain key serverTime");
QVERIFY2(basicConfigurationNotificationMap.contains("serverUuid"), "Notification does not contain key serverUuid");
QVERIFY2(basicConfigurationNotificationMap.contains("debugServerEnabled"), "Notification does not contain key debugServerEnabled");
QVERIFY2(basicConfigurationNotificationMap.contains("timeZone"), "Notification does not contain key timeZone");
QVERIFY2(basicConfigurationNotificationMap.value("timeZone").toString() == newTimeZone, "Notification does not contain the new timeZone");
@ -199,6 +205,7 @@ void TestConfigurations::testServerName()
QVERIFY2(basicConfigurationNotificationMap.contains("serverTime"), "Notification does not contain key serverTime");
QVERIFY2(basicConfigurationNotificationMap.contains("serverUuid"), "Notification does not contain key serverUuid");
QVERIFY2(basicConfigurationNotificationMap.contains("timeZone"), "Notification does not contain key timeZone");
QVERIFY2(basicConfigurationNotificationMap.contains("debugServerEnabled"), "Notification does not contain key debugServerEnabled");
QVERIFY2(basicConfigurationNotificationMap.contains("serverName"), "Notification does not contain key serverName");
QVERIFY2(basicConfigurationNotificationMap.value("serverName").toString() == newServerName, "Notification does not contain the new serverName");
@ -286,6 +293,117 @@ void TestConfigurations::testLanguages()
disableNotifications();
}
void TestConfigurations::testDebugServerConfiguration()
{
enableNotifications();
// Get current configurations
QVariantMap basicConfigurationMap = loadBasicConfiguration();
bool debugServerEnabled = basicConfigurationMap.value("debugServerEnabled").toBool();
qDebug() << "Debug server enabled" << debugServerEnabled;
QSignalSpy notificationSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
// Unchanged debug server
QVariantMap params; QVariant response; QVariantList configurationChangedNotifications;
params.insert("enabled", debugServerEnabled);
response = injectAndWait("Configuration.SetDebugServerEnabled", params);
verifyConfigurationError(response);
// Check notification not emitted
notificationSpy.wait(500);
configurationChangedNotifications = checkNotifications(notificationSpy, "Configuration.BasicConfigurationChanged");
QVERIFY2(configurationChangedNotifications.count() == 0, "Got Configuration.BasicConfigurationChanged notification but should have not.");
// Enable debug server
bool newValue = true;
params.clear(); response.clear(); configurationChangedNotifications.clear();
params.insert("enabled", newValue);
notificationSpy.clear();
response = injectAndWait("Configuration.SetDebugServerEnabled", params);
verifyConfigurationError(response);
// Check notification not emitted
notificationSpy.wait(500);
configurationChangedNotifications = checkNotifications(notificationSpy, "Configuration.BasicConfigurationChanged");
QVariantMap notificationContent = configurationChangedNotifications.first().toMap().value("params").toMap();
QVERIFY2(notificationContent.contains("basicConfiguration"), "Notification does not contain basicConfiguration");
QVERIFY2(configurationChangedNotifications.count() == 1, "Should get only one Configuration.BasicConfigurationChanged notification");
QVariantMap basicConfigurationNotificationMap = notificationContent.value("basicConfiguration").toMap();
QVERIFY2(basicConfigurationNotificationMap.contains("language"), "Notification does not contain key language");
QVERIFY2(basicConfigurationNotificationMap.contains("serverTime"), "Notification does not contain key serverTime");
QVERIFY2(basicConfigurationNotificationMap.contains("serverUuid"), "Notification does not contain key serverUuid");
QVERIFY2(basicConfigurationNotificationMap.contains("timeZone"), "Notification does not contain key timeZone");
QVERIFY2(basicConfigurationNotificationMap.contains("serverName"), "Notification does not contain key serverName");
QVERIFY2(basicConfigurationNotificationMap.contains("debugServerEnabled"), "Notification does not contain key debugServerEnabled");
QVERIFY2(basicConfigurationNotificationMap.value("debugServerEnabled").toBool() == newValue, "Notification does not contain the new debugServerEnabled");
qDebug() << "TestWebserver starting";
foreach (const WebServerConfiguration &config, GuhCore::instance()->configuration()->webServerConfigurations()) {
if (config.port == 3333 && (config.address == QHostAddress("127.0.0.1") || config.address == QHostAddress("0.0.0.0"))) {
qDebug() << "Already have a webserver listening on 127.0.0.1:3333";
return;
}
}
qDebug() << "Creating new webserver instance on 127.0.0.1:3333";
WebServerConfiguration config;
config.id = "Testwebserver for debug server interface";
config.address = QHostAddress("127.0.0.1");
config.port = 3333;
config.sslEnabled = true;
GuhCore::instance()->configuration()->setWebServerConfiguration(config);
// Webserver request
QNetworkAccessManager nam;
connect(&nam, &QNetworkAccessManager::sslErrors, [this, &nam](QNetworkReply* reply, const QList<QSslError> &) {
reply->ignoreSslErrors();
});
QSignalSpy namSpy(&nam, SIGNAL(finished(QNetworkReply*)));
// Check if debug interface is reachable
QNetworkRequest request;
request.setUrl(QUrl("https://localhost:3333/debug/"));
QNetworkReply *reply = nam.get(request);
namSpy.wait();
QVERIFY2(namSpy.count() > 0, "expected response from webserver");
qDebug() << reply->readAll();
bool ok = false;
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
QVERIFY2(ok, "Could not convert statuscode from response to int");
QCOMPARE(statusCode, 200);
reply->deleteLater();
// Disable debug server
params.clear(); response.clear();
params.insert("enabled", false);
response = injectAndWait("Configuration.SetDebugServerEnabled", params);
verifyConfigurationError(response);
// Check if debug interface is not reachable any more
namSpy.clear();
reply = nam.get(request);
namSpy.wait();
QVERIFY2(namSpy.count() > 0, "expected response from webserver");
ok = false;
statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
QVERIFY2(ok, "Could not convert statuscode from response to int");
QCOMPARE(statusCode, 404);
reply->deleteLater();
GuhCore::instance()->configuration()->removeWebServerConfiguration(config.id);
disableNotifications();
}
QVariantMap TestConfigurations::loadBasicConfiguration()
{
QVariant response = injectAndWait("Configuration.GetConfigurations");

View File

@ -68,6 +68,9 @@ private slots:
void getIcons_data();
void getIcons();
void getDebugServer_data();
void getDebugServer();
public slots:
void onSslErrors(const QList<QSslError> &) {
qWarning() << "SSL error";
@ -472,5 +475,160 @@ void TestWebserver::getIcons()
reply->deleteLater();
}
void TestWebserver::getDebugServer_data()
{
QTest::addColumn<QString>("method");
QTest::addColumn<QString>("query");
QTest::addColumn<bool>("serverEnabled");
QTest::addColumn<int>("expectedStatusCode");
// debug enabled
QTest::newRow("GET /debug | server enabled | 200") << "get" << "/debug" << true << 200;
QTest::newRow("OPTIONS /debug | server enabled | 200") << "options" << "/debug" << true << 200;
QTest::newRow("PUT /debug | server enabled | 405") << "put" << "/debug" << true << 405;
QTest::newRow("POST /debug | server enabled | 405") << "post" << "/debug" << true << 405;
QTest::newRow("DELETE /debug | server enabled | 405") << "delete" << "/debug" << true << 405;
// debug disabled
QTest::newRow("GET /debug | server disabled | 404") << "get" << "/debug" << false << 404;
QTest::newRow("OPTIONS /debug | server disabled | 404") << "options" << "/debug" << false << 404;
QTest::newRow("PUT /debug | server disabled | 404") << "put" << "/debug" << false << 404;
QTest::newRow("POST /debug | server disabled | 404") << "post" << "/debug" << false << 404;
QTest::newRow("DELETE /debug | server disabled | 404") << "delete" << "/debug" << false << 404;
// logdb enabled
QTest::newRow("GET /debug/logdb | server enabled | 200") << "get" << "/debug/logdb" << true << 200;
QTest::newRow("OPTIONS /debug/logdb | server enabled | 200") << "options" << "/debug/logdb" << true << 200;
QTest::newRow("PUT /debug/logdb | server enabled | 405") << "put" << "/debug/logdb" << true << 405;
QTest::newRow("POST /debug/logdb | server enabled | 405") << "post" << "/debug/logdb" << true << 405;
QTest::newRow("DELETE /debug/logdb | server enabled | 405") << "delete" << "/debug/logdb" << true << 405;
// logdb disabled
QTest::newRow("GET /debug/logdb | server enabled | 404") << "get" << "/debug/logdb" << false << 404;
QTest::newRow("OPTIONS /debug/logdb | server enabled | 404") << "options" << "/debug/logdb" << false << 404;
QTest::newRow("PUT /debug/logdb | server enabled | 404") << "put" << "/debug/logdb" << false << 404;
QTest::newRow("POST /debug/logdb | server enabled | 404") << "post" << "/debug/logdb" << false << 404;
QTest::newRow("DELETE /debug/logdb | server enabled | 404") << "delete" << "/debug/logdb" << false << 404;
// syslog enabled
QTest::newRow("GET /debug/syslog | server enabled | 200") << "get" << "/debug/syslog" << true << 200;
QTest::newRow("OPTIONS /debug/syslog | server enabled | 200") << "options" << "/debug/logdb" << true << 200;
QTest::newRow("PUT /debug/syslog | server enabled | 405") << "put" << "/debug/logdb" << true << 405;
QTest::newRow("POST /debug/syslog | server enabled | 405") << "post" << "/debug/logdb" << true << 405;
QTest::newRow("DELETE /debug/syslog | server enabled | 405") << "delete" << "/debug/logdb" << true << 405;
// syslog disabled
QTest::newRow("GET /debug/syslog | server enabled | 404") << "get" << "/debug/syslog" << false << 404;
QTest::newRow("OPTIONS /debug/syslog | server enabled | 404") << "options" << "/debug/syslog" << false << 404;
QTest::newRow("PUT /debug/syslog | server enabled | 404") << "put" << "/debug/syslog" << false << 404;
QTest::newRow("POST /debug/syslog | server enabled | 404") << "post" << "/debug/syslog" << false << 404;
QTest::newRow("DELETE /debug/syslog | server enabled | 404") << "delete" << "/debug/syslog" << false << 404;
// settings/guhd enabled
QTest::newRow("GET /debug/settings/guhd | server enabled | 200") << "get" << "/debug/settings/guhd" << true << 200;
QTest::newRow("OPTIONS /debug/settings/guhd | server enabled | 200") << "options" << "/debug/settings/guhd" << true << 200;
QTest::newRow("PUT /debug/settings/guhd | server enabled | 405") << "put" << "/debug/settings/guhd" << true << 405;
QTest::newRow("POST /debug/settings/guhd | server enabled | 405") << "post" << "/debug/settings/guhd" << true << 405;
QTest::newRow("DELETE /debug/settings/guhd | server enabled | 405") << "delete" << "/debug/settings/guhd" << true << 405;
// settings/guhd disabled
QTest::newRow("GET /debug/settings/guhd | server enabled | 404") << "get" << "/debug/settings/guhd" << false << 404;
QTest::newRow("OPTIONS /debug/settings/guhd | server enabled | 404") << "options" << "/debug/settings/guhd" << false << 404;
QTest::newRow("PUT /debug/settings/guhd | server enabled | 404") << "put" << "/debug/settings/guhd" << false << 404;
QTest::newRow("POST /debug/settings/guhd | server enabled | 404") << "post" << "/debug/settings/guhd" << false << 404;
QTest::newRow("DELETE /debug/settings/guhd | server enabled | 404") << "delete" << "/debug/settings/guhd" << false << 404;
// settings/devices enabled
QTest::newRow("GET /debug/settings/devices | server enabled | 200") << "get" << "/debug/settings/devices" << true << 200;
QTest::newRow("OPTIONS /debug/settings/devices | server enabled | 200") << "options" << "/debug/settings/devices" << true << 200;
QTest::newRow("PUT /debug/settings/devices | server enabled | 405") << "put" << "/debug/settings/devices" << true << 405;
QTest::newRow("POST /debug/settings/devices | server enabled | 405") << "post" << "/debug/settings/devices" << true << 405;
QTest::newRow("DELETE /debug/settings/devices | server enabled | 405") << "delete" << "/debug/settings/devices" << true << 405;
// settings/devices disabled
QTest::newRow("GET /debug/settings/devices | server enabled | 404") << "get" << "/debug/settings/devices" << false << 404;
QTest::newRow("OPTIONS /debug/settings/devices | server enabled | 404") << "options" << "/debug/settings/devices" << false << 404;
QTest::newRow("PUT /debug/settings/devices | server enabled | 404") << "put" << "/debug/settings/devices" << false << 404;
QTest::newRow("POST /debug/settings/devices | server enabled | 404") << "post" << "/debug/settings/devices" << false << 404;
QTest::newRow("DELETE /debug/settings/devices | server enabled | 404") << "delete" << "/debug/settings/devices" << false << 404;
// settings/rules enabled
QTest::newRow("GET /debug/settings/rules | server enabled | 200") << "get" << "/debug/settings/rules" << true << 200;
QTest::newRow("OPTIONS /debug/settings/rules | server enabled | 200") << "options" << "/debug/settings/rules" << true << 200;
QTest::newRow("PUT /debug/settings/rules | server enabled | 405") << "put" << "/debug/settings/rules" << true << 405;
QTest::newRow("POST /debug/settings/rules | server enabled | 405") << "post" << "/debug/settings/rules" << true << 405;
QTest::newRow("DELETE /debug/settings/rules | server enabled | 405") << "delete" << "/debug/settings/rules" << true << 405;
// settings/rules disabled
QTest::newRow("GET /debug/settings/rules | server enabled | 404") << "get" << "/debug/settings/rules" << false << 404;
QTest::newRow("OPTIONS /debug/settings/rules | server enabled | 404") << "options" << "/debug/settings/rules" << false << 404;
QTest::newRow("PUT /debug/settings/rules | server enabled | 404") << "put" << "/debug/settings/rules" << false << 404;
QTest::newRow("POST /debug/settings/rules | server enabled | 404") << "post" << "/debug/settings/rules" << false << 404;
QTest::newRow("DELETE /debug/settings/rules | server enabled | 404") << "delete" << "/debug/settings/rules" << false << 404;
// settings/devicestates enabled
QTest::newRow("GET /debug/settings/devicestates | server enabled | 200") << "get" << "/debug/settings/devicestates" << true << 200;
QTest::newRow("OPTIONS /debug/settings/devicestates | server enabled | 200") << "options" << "/debug/settings/devicestates" << true << 200;
QTest::newRow("PUT /debug/settings/devicestates | server enabled | 405") << "put" << "/debug/settings/devicestates" << true << 405;
QTest::newRow("POST /debug/settings/devicestates | server enabled | 405") << "post" << "/debug/settings/devicestates" << true << 405;
QTest::newRow("DELETE /debug/settings/devicestates | server enabled | 405") << "delete" << "/debug/settings/devicestates" << true << 405;
// settings/devicestates disabled
QTest::newRow("GET /debug/settings/devicestates | server enabled | 404") << "get" << "/debug/settings/devicestates" << false << 404;
QTest::newRow("OPTIONS /debug/settings/devicestates | server enabled | 404") << "options" << "/debug/settings/devicestates" << false << 404;
QTest::newRow("PUT /debug/settings/devicestates | server enabled | 404") << "put" << "/debug/settings/devicestates" << false << 404;
QTest::newRow("POST /debug/settings/devicestates | server enabled | 404") << "post" << "/debug/settings/devicestates" << false << 404;
QTest::newRow("DELETE /debug/settings/devicestates | server enabled | 404") << "delete" << "/debug/settings/devicestates" << false << 404;
}
void TestWebserver::getDebugServer()
{
QFETCH(QString, method);
QFETCH(QString, query);
QFETCH(bool, serverEnabled);
QFETCH(int, expectedStatusCode);
// Enable/disable debug server
QVariantMap params; QVariant response;
params.insert("enabled", serverEnabled);
response = injectAndWait("Configuration.SetDebugServerEnabled", params);
verifyConfigurationError(response);
QNetworkAccessManager nam;
bool ok = false;
int statusCode = 0;
QSignalSpy clientSpy(&nam, SIGNAL(finished(QNetworkReply*)));
connect(&nam, &QNetworkAccessManager::sslErrors, [this, &nam](QNetworkReply* reply, const QList<QSslError> &) {
reply->ignoreSslErrors();
});
QNetworkReply *reply = nullptr;
QNetworkRequest request;
request.setUrl(QUrl("https://localhost:3333" + query));
clientSpy.clear();
if (method == "get") {
reply = nam.get(request);
} else if (method == "options") {
reply = nam.sendCustomRequest(request, "OPTIONS");
} else if (method == "post") {
reply = nam.post(request, "");
} else if (method == "delete") {
reply = nam.deleteResource(request);
} else if (method == "put") {
reply = nam.put(request, "");
}
clientSpy.wait();
QVERIFY2(clientSpy.count() == 1, "expected exactly 1 response from webserver");
ok = false;
statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
QVERIFY2(ok, "Could not convert statuscode from response to int");
QCOMPARE(statusCode, expectedStatusCode);
}
#include "testwebserver.moc"
QTEST_MAIN(TestWebserver)