cutelyst 4.3.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;
20
21StaticMap::StaticMap(Cutelyst::Application *parent)
22 : Plugin(parent)
23{
24}
25
27{
28 connect(
29 app, &Cutelyst::Application::beforePrepareAction, this, &StaticMap::beforePrepareAction);
30 return true;
31}
32
33void StaticMap::addStaticMap(const QString &mountPoint, const QString &path, bool append)
34{
35 QString mp = mountPoint;
36 if (!mp.startsWith(u'/')) {
37 mp.prepend(u'/');
38 }
39
40 qCInfo(C_SERVER_SM) << "added mapping for" << mp << "=>" << path;
41
42 m_staticMaps.push_back({mp, path, append});
43 std::sort(m_staticMaps.begin(),
44 m_staticMaps.end(),
45 [](const MountPoint &a, const MountPoint &b) -> bool {
46 return a.mountPoint.size() < b.mountPoint.size();
47 });
48}
49
50void StaticMap::beforePrepareAction(Cutelyst::Context *c, bool *skipMethod)
51{
52 if (*skipMethod) {
53 return;
54 }
55
56 const QString path = c->req()->path();
57 for (const MountPoint &mp : m_staticMaps) {
58 if (path.startsWith(mp.mountPoint)) {
59 if (tryToServeFile(c, mp, path)) {
60 *skipMethod = true;
61 break;
62 }
63 }
64 }
65}
66
67bool StaticMap::tryToServeFile(Cutelyst::Context *c, const MountPoint &mp, const QString &path)
68{
69 QString localPath = path;
70 if (!mp.append) {
71 localPath = path.mid(mp.mountPoint.size());
72 }
73 while (localPath.startsWith(u'/')) {
74 localPath.remove(0, 1);
75 }
76
77 QDir dir(mp.path);
78 QString absFilePath = dir.absoluteFilePath(localPath);
79 if (!QFile::exists(absFilePath)) {
80 return false;
81 }
82
83 return serveFile(c, absFilePath);
84}
85
86bool StaticMap::serveFile(Cutelyst::Context *c, const QString &filename)
87{
88 auto res = c->response();
89 const QDateTime currentDateTime = QFileInfo(filename).lastModified();
90 if (!c->request()->headers().ifModifiedSince(currentDateTime)) {
91 res->setStatus(Response::NotModified);
92 return true;
93 }
94
95 auto file = new QFile(filename);
96 if (file->open(QFile::ReadOnly)) {
97 qCDebug(C_SERVER_SM) << "Serving" << filename;
98 Headers &headers = res->headers();
99
100 // set our open file
101 res->setBody(file);
102
103 // use the extension to match to be faster
105 if (mimeType.isValid()) {
106 headers.setContentType(mimeType.name().toLatin1());
107 }
108
109 headers.setLastModified(currentDateTime);
110 // Tell Firefox & friends its OK to cache, even over SSL
111 headers.setHeader("Cache-Control"_qba, "public"_qba);
112
113 return true;
114 }
115
116 qCWarning(C_SERVER_SM) << "Could not serve" << filename << file->errorString();
117 delete file;
118 return false;
119}
120
121#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:97
Container for HTTP headers.
Definition headers.h:24
QByteArrayList headers(QByteArrayView key) const
Definition headers.cpp:418
QByteArray ifModifiedSince() const noexcept
Definition headers.cpp:205
void setLastModified(const QByteArray &value)
Definition headers.cpp:271
void setContentType(const QByteArray &contentType)
Definition headers.cpp:76
void setHeader(const QByteArray &key, const QByteArray &value)
Definition headers.cpp:436
Base class for Cutelyst Plugins.
Definition plugin.h:25
Headers headers() const noexcept
Definition request.cpp:312
virtual bool setup(Cutelyst::Application *app) override
Definition staticmap.cpp:26
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