From 7f2d6bde9a6d802bb001e5d3c957643a9b55e274 Mon Sep 17 00:00:00 2001 From: redPanther Date: Sun, 29 Jan 2017 21:20:12 +0100 Subject: [PATCH] add http error pages (#381) * implement 404 for webserver - this is a quick hack, should be refactored later * add http error pages ... design is more a placebo ;-) * tune errorpages fix some cgi related stuff, now only python is possible executing and reading python file is possilbe, but it cannot receive any data from webui * fix typo * fix another typo --- assets/webconfig/errorpages/403.html | 4 ++ assets/webconfig/errorpages/404.html | 4 ++ assets/webconfig/errorpages/405.html | 4 ++ assets/webconfig/errorpages/500.html | 4 ++ assets/webconfig/errorpages/footer.html | 4 ++ assets/webconfig/errorpages/header.html | 30 +++++++++++ assets/webconfig/index.html | 47 ++++++++--------- assets/webconfig/server_scripts/demo.sh | 5 -- include/utils/Process.h | 5 +- libsrc/utils/Process.cpp | 19 +++---- libsrc/webconfig/CgiHandler.cpp | 20 +++++--- libsrc/webconfig/CgiHandler.h | 14 ++--- libsrc/webconfig/StaticFileServing.cpp | 68 ++++++++++++++++--------- libsrc/webconfig/StaticFileServing.h | 2 + src/hyperiond/main.cpp | 10 +++- 15 files changed, 161 insertions(+), 79 deletions(-) create mode 100644 assets/webconfig/errorpages/403.html create mode 100644 assets/webconfig/errorpages/404.html create mode 100644 assets/webconfig/errorpages/405.html create mode 100644 assets/webconfig/errorpages/500.html create mode 100644 assets/webconfig/errorpages/footer.html create mode 100644 assets/webconfig/errorpages/header.html delete mode 100644 assets/webconfig/server_scripts/demo.sh diff --git a/assets/webconfig/errorpages/403.html b/assets/webconfig/errorpages/403.html new file mode 100644 index 00000000..5fdf3c1e --- /dev/null +++ b/assets/webconfig/errorpages/403.html @@ -0,0 +1,4 @@ +
+
403 Forbidden
+
{MESSAGE}
+
diff --git a/assets/webconfig/errorpages/404.html b/assets/webconfig/errorpages/404.html new file mode 100644 index 00000000..9d7e27b0 --- /dev/null +++ b/assets/webconfig/errorpages/404.html @@ -0,0 +1,4 @@ +
+
404 Page not found
+
{MESSAGE}
+
diff --git a/assets/webconfig/errorpages/405.html b/assets/webconfig/errorpages/405.html new file mode 100644 index 00000000..a21d1af8 --- /dev/null +++ b/assets/webconfig/errorpages/405.html @@ -0,0 +1,4 @@ +
+
405 Method not allowed
+
{MESSAGE}
+
diff --git a/assets/webconfig/errorpages/500.html b/assets/webconfig/errorpages/500.html new file mode 100644 index 00000000..a146ae21 --- /dev/null +++ b/assets/webconfig/errorpages/500.html @@ -0,0 +1,4 @@ +
+
500 Internal server error
+
{MESSAGE}
+
diff --git a/assets/webconfig/errorpages/footer.html b/assets/webconfig/errorpages/footer.html new file mode 100644 index 00000000..63c488a1 --- /dev/null +++ b/assets/webconfig/errorpages/footer.html @@ -0,0 +1,4 @@ + + + + diff --git a/assets/webconfig/errorpages/header.html b/assets/webconfig/errorpages/header.html new file mode 100644 index 00000000..c38f4cb8 --- /dev/null +++ b/assets/webconfig/errorpages/header.html @@ -0,0 +1,30 @@ + + + + Hyperion WebServer - Error + + + + + + + + + + + + + + + + + + + + +
diff --git a/assets/webconfig/index.html b/assets/webconfig/index.html index bb396e8b..44a6434c 100644 --- a/assets/webconfig/index.html +++ b/assets/webconfig/index.html @@ -1,56 +1,55 @@ - + - - Hyperion - WebUI + Hyperion - Error - + - - + + - - + + - - + + - + - - - - - - + + + + + + - + - + - + - - + + - + @@ -58,8 +57,6 @@ - - diff --git a/assets/webconfig/server_scripts/demo.sh b/assets/webconfig/server_scripts/demo.sh deleted file mode 100644 index adfa2ccd..00000000 --- a/assets/webconfig/server_scripts/demo.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -echo "hello world" -pwd - diff --git a/include/utils/Process.h b/include/utils/Process.h index dd1055c9..6437656a 100644 --- a/include/utils/Process.h +++ b/include/utils/Process.h @@ -1,10 +1,11 @@ #pragma once -#include +#include +#include namespace Process { void restartHyperion(bool asNewProcess=false); -std::string command_exec(const char* cmd); +QByteArray command_exec(QString cmd, QByteArray data=""); }; \ No newline at end of file diff --git a/libsrc/utils/Process.cpp b/libsrc/utils/Process.cpp index 2f632d9d..8005e919 100644 --- a/libsrc/utils/Process.cpp +++ b/libsrc/utils/Process.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -37,21 +38,21 @@ void restartHyperion(bool asNewProcess) Error(log, "error while restarting hyperion"); } -std::string command_exec(const char* cmd) +QByteArray command_exec(QString cmd, QByteArray data) { char buffer[128]; std::string result = ""; - std::shared_ptr pipe(popen(cmd, "r"), pclose); + + std::shared_ptr pipe(popen(cmd.toLocal8Bit().constData(), "r"), pclose); if (pipe) { - while (!feof(pipe.get())) - { - if (fgets(buffer, 128, pipe.get()) != NULL) - result += buffer; + while (!feof(pipe.get())) + { + if (fgets(buffer, 128, pipe.get()) != NULL) + result += buffer; + } } - } - - return result; + return result.c_str(); } }; \ No newline at end of file diff --git a/libsrc/webconfig/CgiHandler.cpp b/libsrc/webconfig/CgiHandler.cpp index 20b52b46..ff09192f 100644 --- a/libsrc/webconfig/CgiHandler.cpp +++ b/libsrc/webconfig/CgiHandler.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "CgiHandler.h" #include "QtHttpHeader.h" @@ -18,6 +19,7 @@ CgiHandler::CgiHandler (Hyperion * hyperion, QString baseUrl, QObject * parent) , _args(QStringList()) , _hyperionConfig(_hyperion->getQJsonConfig()) , _baseUrl(baseUrl) + , _log(Logger::getInstance("WEBSERVER")) { } @@ -123,22 +125,26 @@ void CgiHandler::cmd_runscript() // relative path not allowed if (scriptFilePath.indexOf("..") >=0) { + Error( _log, "relative path not allowed (%s)", scriptFilePath.toStdString().c_str()); throw 1; } scriptFilePath = _baseUrl+"/server_scripts/"+scriptFilePath; - QString interpreter = ""; - if (scriptFilePath.endsWith(".sh")) interpreter = "sh"; - if (scriptFilePath.endsWith(".py")) interpreter = "python"; - - if (QFile::exists(scriptFilePath) && !interpreter.isEmpty()) + + if (QFile::exists(scriptFilePath) && scriptFilePath.endsWith(".py") ) { - QByteArray data = Process::command_exec(QString(interpreter + " " + scriptFilePath).toUtf8().constData()).c_str(); - + QtHttpPostData postData = _request->getPostData(); + QByteArray inputData; // should be filled with post data + QByteArray data = Process::command_exec("python " + scriptFilePath, inputData); _reply->addHeader ("Content-Type", "text/plain"); _reply->appendRawData (data); throw 0; } + else + { + Error( _log, "script %s doesn't exists or is no python file", scriptFilePath.toStdString().c_str()); + } + throw 1; } } diff --git a/libsrc/webconfig/CgiHandler.h b/libsrc/webconfig/CgiHandler.h index 284c4518..2cb03107 100644 --- a/libsrc/webconfig/CgiHandler.h +++ b/libsrc/webconfig/CgiHandler.h @@ -6,6 +6,7 @@ #include #include +#include #include "QtHttpReply.h" #include "QtHttpRequest.h" @@ -26,12 +27,13 @@ public: void cmd_runscript (); private: - Hyperion* _hyperion; - QtHttpReply * _reply; - QtHttpRequest * _request; - QStringList _args; - const QJsonObject &_hyperionConfig; - const QString _baseUrl; + Hyperion* _hyperion; + QtHttpReply * _reply; + QtHttpRequest * _request; + QStringList _args; + const QJsonObject & _hyperionConfig; + const QString _baseUrl; + Logger * _log; }; #endif // CGIHANDLER_H diff --git a/libsrc/webconfig/StaticFileServing.cpp b/libsrc/webconfig/StaticFileServing.cpp index 8796b365..2d49d7ba 100644 --- a/libsrc/webconfig/StaticFileServing.cpp +++ b/libsrc/webconfig/StaticFileServing.cpp @@ -11,7 +11,7 @@ #include #include #include - +#include StaticFileServing::StaticFileServing (Hyperion *hyperion, QString baseUrl, quint16 port, QObject * parent) : QObject (parent) @@ -45,10 +45,7 @@ void StaticFileServing::onServerStarted (quint16 port) { Info(_log, "started on port %d name \"%s\"", port ,_server->getServerName().toStdString().c_str()); - const std::string mDNSDescr = ( _server->getServerName().toStdString() - + "@" + - QHostInfo::localHostName().toStdString() - ); + const std::string mDNSDescr = (_server->getServerName().toStdString() + "@" + QHostInfo::localHostName().toStdString()); BonjourServiceRegister *bonjourRegister_http = new BonjourServiceRegister(); bonjourRegister_http->registerService( @@ -67,17 +64,39 @@ void StaticFileServing::onServerError (QString msg) Error(_log, "%s", msg.toStdString().c_str()); } -static inline void printErrorToReply (QtHttpReply * reply, QString errorMessage) +void StaticFileServing::printErrorToReply (QtHttpReply * reply, QtHttpReply::StatusCode code, QString errorMessage) { - reply->addHeader ("Content-Type", QByteArrayLiteral ("text/plain")); - reply->appendRawData (errorMessage.toLocal8Bit ()); -} + reply->setStatusCode(code); + reply->addHeader ("Content-Type", QByteArrayLiteral ("text/html")); + QFile errorPageHeader(_baseUrl % "/errorpages/header.html" ); + QFile errorPageFooter(_baseUrl % "/errorpages/footer.html" ); + QFile errorPage (_baseUrl % "/errorpages/" % QString::number((int)code) % ".html" ); -static inline void printError404ToReply (QtHttpReply * reply, QString errorMessage) -{ - reply->setStatusCode(QtHttpReply::NotFound); - reply->addHeader ("Content-Type", QByteArrayLiteral ("text/plain")); - reply->appendRawData (errorMessage.toLocal8Bit ()); + if (errorPageHeader.open (QFile::ReadOnly)) + { + QByteArray data = errorPageHeader.readAll(); + reply->appendRawData (data); + errorPageHeader.close (); + } + + if (errorPage.open (QFile::ReadOnly)) + { + QByteArray data = errorPage.readAll(); + data = data.replace("{MESSAGE}", errorMessage.toLocal8Bit() ); + reply->appendRawData (data); + errorPage.close (); + } + else + { + reply->appendRawData (QString(QString::number(code) + " - " +errorMessage).toLocal8Bit()); + } + + if (errorPageFooter.open (QFile::ReadOnly)) + { + QByteArray data = errorPageFooter.readAll (); + reply->appendRawData (data); + errorPageFooter.close (); + } } void StaticFileServing::onRequestNeedsReply (QtHttpRequest * request, QtHttpReply * reply) @@ -94,16 +113,17 @@ void StaticFileServing::onRequestNeedsReply (QtHttpRequest * request, QtHttpRepl uri_parts.removeAt(0); try { - if (command == QStringLiteral ("POST")) - { - QString postData = request->getRawData(); - uri_parts.append(postData.split('&', QString::SkipEmptyParts)); - } _cgi.exec(uri_parts, request, reply); } - catch(...) + catch(int err) { - printErrorToReply (reply, "script failed (" % path % ")"); + Error(_log,"Exception while executing cgi %s : %d", path.toStdString().c_str(), err); + printErrorToReply (reply, QtHttpReply::InternalError, "script failed (" % path % ")"); + } + catch(std::exception &e) + { + Error(_log,"Exception while executing cgi %s : %s", path.toStdString().c_str(), e.what()); + printErrorToReply (reply, QtHttpReply::InternalError, "script failed (" % path % ")"); } return; } @@ -136,17 +156,17 @@ void StaticFileServing::onRequestNeedsReply (QtHttpRequest * request, QtHttpRepl } else { - printErrorToReply (reply, "Requested file " % path % " couldn't be open for reading !"); + printErrorToReply (reply, QtHttpReply::Forbidden ,"Requested file: " % path); } } else { - printError404ToReply (reply, "404 Not Found\n" % path % " couldn't be found !"); + printErrorToReply (reply, QtHttpReply::NotFound, "Requested file: " % path); } } else { - printErrorToReply (reply, "Unhandled HTTP/1.1 method " % command % " on web server !"); + printErrorToReply (reply, QtHttpReply::MethodNotAllowed,"Unhandled HTTP/1.1 method " % command); } } diff --git a/libsrc/webconfig/StaticFileServing.h b/libsrc/webconfig/StaticFileServing.h index 99daf833..8c82b69c 100644 --- a/libsrc/webconfig/StaticFileServing.h +++ b/libsrc/webconfig/StaticFileServing.h @@ -35,6 +35,8 @@ private: CgiHandler _cgi; Logger * _log; + void printErrorToReply (QtHttpReply * reply, QtHttpReply::StatusCode code, QString errorMessage); + }; #endif // STATICFILESERVING_H diff --git a/src/hyperiond/main.cpp b/src/hyperiond/main.cpp index 6f3af2eb..2a313f88 100644 --- a/src/hyperiond/main.cpp +++ b/src/hyperiond/main.cpp @@ -33,6 +33,12 @@ using namespace commandline; void signal_handler(const int signum) { + if(signum == SIGCHLD) + { + // only quit when a registered child process is gone + // currently this feature is not active ... + return; + } QCoreApplication::quit(); // reset signal handler to default (in case this handler is not capable of stopping) @@ -42,7 +48,8 @@ void signal_handler(const int signum) void startNewHyperion(int parentPid, std::string hyperionFile, std::string configFile) { - if ( fork() == 0 ) + pid_t childPid = fork(); // child pid should store elsewhere for later use + if ( childPid == 0 ) { sleep(3); execl(hyperionFile.c_str(), hyperionFile.c_str(), "--parent", QString::number(parentPid).toStdString().c_str(), configFile.c_str(), NULL); @@ -62,6 +69,7 @@ int main(int argc, char** argv) signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); + signal(SIGABRT, signal_handler); signal(SIGCHLD, signal_handler); signal(SIGPIPE, signal_handler);