Implemented directory listing

This commit is contained in:
Daniel Brunner
2018-10-04 23:32:24 +02:00
parent 2d33897c2a
commit 7f5df336e0
7 changed files with 131 additions and 6 deletions

View File

@ -1,14 +1,36 @@
#include "fileserverapplication.h"
#include <QJsonObject>
#include <QJsonValue>
#include <QFileInfo>
#include <QDir>
#include <QUrl>
#include <QFile>
#include <QDebug>
#include <memory>
#include "webserver.h"
#include "httprequest.h"
#include "httpresponse.h"
#include "httpclientconnection.h"
#include "httpnotfoundexception.h"
#include "httpexception.h"
const QString FileserverApplication::SERVER_NAME(QStringLiteral("Fileserver 1.0"));
const QDir FileserverApplication::PLUGIN_RESOURCES_DIR(QStringLiteral(":/webserver/plugins/fileserverplugin"));
FileserverApplication::FileserverApplication(const QJsonObject &config, WebServer &webServer) :
WebApplication(&webServer), m_webServer(webServer)
{
Q_UNUSED(config)
if(!config.contains(QStringLiteral("rootPath")))
throw std::runtime_error("Fileserver application does not contain rootPath");
const auto rootPathVal = config.value(QStringLiteral("rootPath"));
if(!rootPathVal.isString())
throw std::runtime_error("Fileserver application rootPath is not a string");
m_rootPath = QDir(rootPathVal.toString());
}
void FileserverApplication::start()
@ -17,8 +39,97 @@ void FileserverApplication::start()
void FileserverApplication::handleRequest(HttpClientConnection *connection, const HttpRequest &request)
{
HttpResponse response;
response.protocol = request.protocol;
response.statusCode = HttpResponse::StatusCode::OK;
connection->sendResponse(response, "Hello from FileserverApplication: " + request.path);
static const QString pluginPrefix(QStringLiteral("/:fileserverplugin/"));
const auto pluginResource = request.path.startsWith(pluginPrefix);
const QFileInfo fileInfo((pluginResource ? PLUGIN_RESOURCES_DIR : m_rootPath)
.absoluteFilePath(request.path.mid(pluginResource ? pluginPrefix.length() : 1)));
if(!fileInfo.exists())
throw HttpNotFoundException(request);
if(fileInfo.isDir())
{
if(!request.path.endsWith(QLatin1Char('/')))
{
const QString newUrl = request.path + '/';
HttpResponse response;
response.protocol = request.protocol;
response.statusCode = HttpResponse::StatusCode::MovedPermanently;
response.headers.insert(HttpResponse::HEADER_SERVER, SERVER_NAME);
response.headers.insert(HttpResponse::HEADER_LOCATION, newUrl);
connection->sendResponse(response, QStringLiteral("<a href=\"%0\">%1</a>").arg(QString(QUrl::toPercentEncoding(newUrl)).toHtmlEscaped()).arg(tr("Moved.")));
return;
}
const QDir dir(fileInfo.absoluteFilePath());
QString content;
static const QString line(QStringLiteral("<tr><td><img src=\"/:fileserverplugin/images/%0\" /> <a href=\"%1\">%2</a></td><td>%3</td></tr>"));
if(request.path != QStringLiteral("/"))
content.append(line.arg(QStringLiteral("back.png")).arg(QStringLiteral("..")).arg(tr("Parent directory")).arg(QString()));
for(auto info : dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot))
content.append(line
.arg(info.isDir() ? QStringLiteral("folder.png") : QStringLiteral("text.png"))
.arg(QString(QUrl::toPercentEncoding(info.fileName())).toHtmlEscaped())
.arg(info.fileName().toHtmlEscaped())
.arg(info.isDir() ? QString() : formatSize(info.size())));
HttpResponse response;
response.protocol = request.protocol;
response.statusCode = HttpResponse::StatusCode::OK;
response.headers.insert(HttpResponse::HEADER_SERVER, SERVER_NAME);
connection->sendResponse(response, QStringLiteral("<!doctype html>"
"<html lang=\"en\">"
"<head>"
"<meta charset=\"utf-8\">"
"<link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css\" integrity=\"sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO\" crossorigin=\"anonymous\">"
"</head>"
"<body>"
"<div class=\"container\">"
"<table class=\"table\">"
"<thead>"
"<tr>"
"<th>Filename</th>"
"<th>Size</th>"
"</tr>"
"</thead>"
"<tbody>"
"%0"
"</tbody>"
"</table>"
"</div>"
"</body>"
"</html>").arg(content));
}
else if(fileInfo.isFile())
{
std::unique_ptr<QFileDevice> file = std::make_unique<QFile>(fileInfo.filePath());
if(!file->open(QIODevice::ReadOnly))
throw HttpException(request, tr("Could not open requested file \"%0\" because %1").arg(request.path, file->errorString()).toStdString());
HttpResponse response;
response.protocol = request.protocol;
response.statusCode = HttpResponse::StatusCode::OK;
response.headers.insert(HttpResponse::HEADER_SERVER, SERVER_NAME);
connection->sendResponse(response, std::move(file));
}
}
QString FileserverApplication::formatSize(qint64 size)
{
static const QStringList suffixes { QStringLiteral("B"), QStringLiteral("kB"), QStringLiteral("MB"), QStringLiteral("GB"), QStringLiteral("TB") };
static const int stepSize = 1024;
for(const auto &suffix : suffixes)
{
if(size < stepSize)
return QStringLiteral("%0 %1").arg(size).arg(suffix);
size /= stepSize;
}
return QStringLiteral("%0 %1").arg(size).arg(suffixes.last());
}

View File

@ -2,6 +2,8 @@
#include "webapplication.h"
#include <QDir>
class QJsonObject;
class WebServer;
@ -9,6 +11,8 @@ class WebServer;
class FileserverApplication : public WebApplication
{
Q_OBJECT
static const QString SERVER_NAME;
static const QDir PLUGIN_RESOURCES_DIR;
public:
FileserverApplication(const QJsonObject &config, WebServer &webServer);
@ -18,5 +22,8 @@ public:
void handleRequest(HttpClientConnection *connection, const HttpRequest &request) Q_DECL_OVERRIDE;
private:
static QString formatSize(qint64 size);
WebServer &m_webServer;
QDir m_rootPath;
};

View File

@ -10,7 +10,7 @@ SOURCES += fileserverplugin.cpp \
FORMS +=
RESOURCES +=
RESOURCES += fileserverplugin_resources.qrc
TRANSLATIONS +=

View File

@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/webserver/plugins/fileserverplugin">
<file>images/back.png</file>
<file>images/folder.png</file>
<file>images/text.png</file>
</qresource>
</RCC>

BIN
images/back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

BIN
images/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

BIN
images/text.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B