Implemented directory listing
This commit is contained in:
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -10,7 +10,7 @@ SOURCES += fileserverplugin.cpp \
|
||||
|
||||
FORMS +=
|
||||
|
||||
RESOURCES +=
|
||||
RESOURCES += fileserverplugin_resources.qrc
|
||||
|
||||
TRANSLATIONS +=
|
||||
|
||||
|
7
fileserverplugin_resources.qrc
Normal file
7
fileserverplugin_resources.qrc
Normal 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
BIN
images/back.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 308 B |
BIN
images/folder.png
Normal file
BIN
images/folder.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 295 B |
BIN
images/text.png
Normal file
BIN
images/text.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 288 B |
Reference in New Issue
Block a user