cutelyst  5.0.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 using namespace Qt::Literals::StringLiterals;
21 
22 StaticMap::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 
34 void 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 
49 void 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 
66 bool 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 
85 bool 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
103  QMimeType mimeType = m_db.mimeTypeForFile(filename, QMimeDatabase::MatchExtension);
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"
Request request
Definition: context.h:72
QString & prepend(QChar ch)
qsizetype size() const const
Container for HTTP headers.
Definition: headers.h:23
void setLastModified(const QByteArray &value)
Definition: headers.cpp:298
bool exists() const const
Request req
Definition: context.h:67
The Cutelyst Context.
Definition: context.h:42
void setContentType(const QByteArray &contentType)
Definition: headers.cpp:103
Headers headers() const noexcept
Definition: request.cpp:312
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
void beforePrepareAction(Cutelyst::Context *c, bool *skipMethod)
virtual bool setup(Cutelyst::Application *app) override
Definition: staticmap.cpp:27
The Cutelyst namespace holds all public Cutelyst API.
QDateTime lastModified() const const
void push_back(QChar ch)
bool isValid() const const
QByteArray ifModifiedSince() const noexcept
Definition: headers.cpp:232
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString mid(qsizetype position, qsizetype n) const const
Base class for Cutelyst Plugins.
Definition: plugin.h:24
The Cutelyst application.
Definition: application.h:72
Response * response() const noexcept
Definition: context.cpp:98
QByteArrayList headers(QAnyStringView key) const
Definition: headers.cpp:448
void setHeader(const QByteArray &key, const QByteArray &value)
Definition: headers.cpp:466