cutelyst 5.0.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
staticmap.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2016-2023 Daniel Nicoletti <dantti12@gmail.com>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5#include "staticmap.h"
6
7#include "socket.h"
8
9#include <Cutelyst/Application>
10#include <Cutelyst/Request>
11#include <Cutelyst/Response>
12
13#include <QDir>
14#include <QFile>
15#include <QLoggingCategory>
16
17Q_LOGGING_CATEGORY(C_SERVER_SM, "cutelyst.server.staticmap", QtWarningMsg)
18
19using namespace Cutelyst;
20using namespace Qt::Literals::StringLiterals;
21
22StaticMap::StaticMap(Cutelyst::Application *parent)
23 : Plugin(parent)
24{
25}
26
28{
29 connect(
30 app, &Cutelyst::Application::beforePrepareAction, this, &StaticMap::beforePrepareAction);
31 return true;
32}
33
34void StaticMap::addStaticMap(const QString &mountPoint, const QString &path, bool append)
35{
36 QString mp = mountPoint;
37 if (!mp.startsWith(u'/')) {
38 mp.prepend(u'/');
39 }
40
41 qCInfo(C_SERVER_SM) << "added mapping for" << mp << "=>" << path;
42
43 m_staticMaps.push_back({mp, path, append});
44 std::ranges::sort(m_staticMaps, [](const MountPoint &a, const MountPoint &b) -> bool {
45 return a.mountPoint.size() < b.mountPoint.size();
46 });
47}
48
49void StaticMap::beforePrepareAction(Cutelyst::Context *c, bool *skipMethod)
50{
51 if (*skipMethod) {
52 return;
53 }
54
55 const QString path = c->req()->path();
56 for (const MountPoint &mp : m_staticMaps) {
57 if (path.startsWith(mp.mountPoint)) {
58 if (tryToServeFile(c, mp, path)) {
59 *skipMethod = true;
60 break;
61 }
62 }
63 }
64}
65
66bool StaticMap::tryToServeFile(Cutelyst::Context *c, const MountPoint &mp, const QString &path)
67{
68 QString localPath = path;
69 if (!mp.append) {
70 localPath = path.mid(mp.mountPoint.size());
71 }
72 while (localPath.startsWith(u'/')) {
73 localPath.remove(0, 1);
74 }
75
76 QDir dir(mp.path);
77 QString absFilePath = dir.absoluteFilePath(localPath);
78 if (!QFile::exists(absFilePath)) {
79 return false;
80 }
81
82 return serveFile(c, absFilePath);
83}
84
85bool StaticMap::serveFile(Cutelyst::Context *c, const QString &filename)
86{
87 auto res = c->response();
88 const QDateTime currentDateTime = QFileInfo(filename).lastModified();
89 if (!c->request()->headers().ifModifiedSince(currentDateTime)) {
90 res->setStatus(Response::NotModified);
91 return true;
92 }
93
94 auto file = new QFile(filename);
95 if (file->open(QFile::ReadOnly)) {
96 qCDebug(C_SERVER_SM) << "Serving" << filename;
97 Headers &headers = res->headers();
98
99 // set our open file
100 res->setBody(file);
101
102 // use the extension to match to be faster
104 if (mimeType.isValid()) {
105 headers.setContentType(mimeType.name().toLatin1());
106 }
107
108 headers.setLastModified(currentDateTime);
109 // Tell Firefox & friends its OK to cache, even over SSL
110 headers.setHeader("Cache-Control"_ba, "public"_ba);
111
112 return true;
113 }
114
115 qCWarning(C_SERVER_SM) << "Could not serve" << filename << file->errorString();
116 delete file;
117 return false;
118}
119
120#include "moc_staticmap.cpp"
The Cutelyst application.
Definition application.h:66
void beforePrepareAction(Cutelyst::Context *c, bool *skipMethod)
The Cutelyst Context.
Definition context.h:42
Request * request
Definition context.h:71
Request * req
Definition context.h:66
Response * response() const noexcept
Definition context.cpp:98
Container for HTTP headers.
Definition headers.h:24
QByteArray ifModifiedSince() const noexcept
Definition headers.cpp:206
void setLastModified(const QByteArray &value)
Definition headers.cpp:272
void setContentType(const QByteArray &contentType)
Definition headers.cpp:77
void setHeader(const QByteArray &key, const QByteArray &value)
Definition headers.cpp:440
QByteArrayList headers(QAnyStringView key) const
Definition headers.cpp:422
Base class for Cutelyst Plugins.
Definition plugin.h:25
Headers headers() const noexcept
Definition request.cpp:313
virtual bool setup(Cutelyst::Application *app) override
Definition staticmap.cpp:27
The Cutelyst namespace holds all public Cutelyst API.
bool exists() const const
QDateTime lastModified() const const
QMimeType mimeTypeForFile(const QFileInfo &fileInfo, QMimeDatabase::MatchMode mode) const const
bool isValid() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString mid(qsizetype position, qsizetype n) const const
QString & prepend(QChar ch)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const