cutelyst  4.5.1
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 
17 Q_LOGGING_CATEGORY(C_SERVER_SM, "cutelyst.server.staticmap", QtWarningMsg)
18 
19 using namespace Cutelyst;
20 
21 StaticMap::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 
33 void 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 
50 void 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 
67 bool 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 
86 bool 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
104  QMimeType mimeType = m_db.mimeTypeForFile(filename, QMimeDatabase::MatchExtension);
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