Cutelyst  3.5.0
engine.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2013-2022 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "engine_p.h"
6 
7 #include "context_p.h"
8 
9 #include "common.h"
10 #include "request_p.h"
11 #include "application.h"
12 #include "response_p.h"
13 #include "context_p.h"
14 
15 #include <QUrl>
16 #include <QSettings>
17 #include <QDir>
18 #include <QThread>
19 #include <QByteArray>
20 #include <QJsonDocument>
21 
22 using namespace Cutelyst;
23 
44 Engine::Engine(Cutelyst::Application *app, int workerCore, const QVariantMap &opts)
45  : d_ptr(new EnginePrivate)
46 {
47  Q_D(Engine);
48 
49  connect(this, &Engine::processRequestAsync, this, &Engine::processRequest, Qt::QueuedConnection);
50 
51  d->opts = opts;
52  d->workerCore = workerCore;
53 
54  // If workerCore is greater than 0 we need a new application instance
55  if (workerCore) {
56  auto newApp = qobject_cast<Application *>(app->metaObject()->newInstance());
57  if (!newApp) {
58  qFatal("*** FATAL *** Could not create a NEW instance of your Cutelyst::Application, "
59  "make sure your constructor has Q_INVOKABLE macro or disable threaded mode.");
60  }
61  d->app = newApp;
62 
63  // To make easier for engines to clean up
64  // the NEW app must be a child of it
65  d->app->setParent(this);
66  } else {
67  d->app = app;
68  }
69 }
70 
71 Engine::~Engine()
72 {
73  delete d_ptr;
74 }
75 
81 {
82  Q_D(const Engine);
83  Q_ASSERT(d->app);
84  return d->app;
85 }
86 
107 {
108  Q_D(const Engine);
109  return d->workerCore;
110 }
111 
113 {
114  Q_D(Engine);
115 
116  if (thread() != QThread::currentThread()) {
117  qCCritical(CUTELYST_ENGINE) << "Cannot init application on a different thread";
118  return false;
119  }
120 
121  if (!d->app->setup(this)) {
122  qCCritical(CUTELYST_ENGINE) << "Failed to setup application";
123  return false;
124  }
125 
126  return true;
127 }
128 
130 {
131  Q_D(Engine);
132 
133  if (!d->app) {
134  qCCritical(CUTELYST_ENGINE) << "Failed to postForkApplication on a null application";
135  return false;
136  }
137 
139 
140  return d->app->enginePostFork();
141 }
142 
143 quint64 Engine::time()
144 {
145  return quint64(QDateTime::currentMSecsSinceEpoch() * 1000);
146 }
147 
148 const char *Engine::httpStatusMessage(quint16 status, int *len)
149 {
150  const char *ret;
151  switch (status) {
152  case Response::OK:
153  ret = "HTTP/1.1 200 OK";
154  break;
155  case Response::Found:
156  ret = "HTTP/1.1 302 Found";
157  break;
158  case Response::NotFound:
159  ret = "HTTP/1.1 404 Not Found";
160  break;
161  case Response::InternalServerError:
162  ret = "HTTP/1.1 500 Internal Server Error";
163  break;
164  case Response::MovedPermanently:
165  ret = "HTTP/1.1 301 Moved Permanently";
166  break;
167  case Response::NotModified:
168  ret = "HTTP/1.1 304 Not Modified";
169  break;
170  case Response::SeeOther:
171  ret = "HTTP/1.1 303 See Other";
172  break;
173  case Response::Forbidden:
174  ret = "HTTP/1.1 403 Forbidden";
175  break;
176  case Response::TemporaryRedirect:
177  ret = "HTTP/1.1 307 Temporary Redirect";
178  break;
179  case Response::Unauthorized:
180  ret = "HTTP/1.1 401 Unauthorized";
181  break;
182  case Response::BadRequest:
183  ret = "HTTP/1.1 400 Bad Request";
184  break;
185  case Response::MethodNotAllowed:
186  ret = "HTTP/1.1 405 Method Not Allowed";
187  break;
188  case Response::RequestTimeout:
189  ret = "HTTP/1.1 408 Request Timeout";
190  break;
191  case Response::Continue:
192  ret = "HTTP/1.1 100 Continue";
193  break;
194  case Response::SwitchingProtocols:
195  ret = "HTTP/1.1 101 Switching Protocols";
196  break;
197  case Response::Created:
198  ret = "HTTP/1.1 201 Created";
199  break;
200  case Response::Accepted:
201  ret = "HTTP/1.1 202 Accepted";
202  break;
203  case Response::NonAuthoritativeInformation:
204  ret = "HTTP/1.1 203 Non-Authoritative Information";
205  break;
206  case Response::NoContent:
207  ret = "HTTP/1.1 204 No Content";
208  break;
209  case Response::ResetContent:
210  ret = "HTTP/1.1 205 Reset Content";
211  break;
212  case Response::PartialContent:
213  ret = "HTTP/1.1 206 Partial Content";
214  break;
215  case Response::MultipleChoices:
216  ret = "HTTP/1.1 300 Multiple Choices";
217  break;
218  case Response::UseProxy:
219  ret = "HTTP/1.1 305 Use Proxy";
220  break;
221  case Response::PaymentRequired:
222  ret = "HTTP/1.1 402 Payment Required";
223  break;
224  case Response::NotAcceptable:
225  ret = "HTTP/1.1 406 Not Acceptable";
226  break;
227  case Response::ProxyAuthenticationRequired:
228  ret = "HTTP/1.1 407 Proxy Authentication Required";
229  break;
230  case Response::Conflict:
231  ret = "HTTP/1.1 409 Conflict";
232  break;
233  case Response::Gone:
234  ret = "HTTP/1.1 410 Gone";
235  break;
236  case Response::LengthRequired:
237  ret = "HTTP/1.1 411 Length Required";
238  break;
239  case Response::PreconditionFailed:
240  ret = "HTTP/1.1 412 Precondition Failed";
241  break;
242  case Response::RequestEntityTooLarge:
243  ret = "HTTP/1.1 413 Request Entity Too Large";
244  break;
245  case Response::RequestURITooLong:
246  ret = "HTTP/1.1 414 Request-URI Too Long";
247  break;
248  case Response::UnsupportedMediaType:
249  ret = "HTTP/1.1 415 Unsupported Media Type";
250  break;
251  case Response::RequestedRangeNotSatisfiable:
252  ret = "HTTP/1.1 416 Requested Range Not Satisfiable";
253  break;
254  case Response::ExpectationFailed:
255  ret = "HTTP/1.1 417 Expectation Failed";
256  break;
257  case Response::NotImplemented:
258  ret = "HTTP/1.1 501 Not Implemented";
259  break;
260  case Response::BadGateway:
261  ret = "HTTP/1.1 502 Bad Gateway";
262  break;
263  case Response::ServiceUnavailable:
264  ret = "HTTP/1.1 503 Service Unavailable";
265  break;
266  case Response::MultiStatus:
267  ret = "HTTP/1.1 207 Multi-Status";
268  break;
269  case Response::GatewayTimeout:
270  ret = "HTTP/1.1 504 Gateway Timeout";
271  break;
272  case Response::HTTPVersionNotSupported:
273  ret = "HTTP/1.1 505 HTTP Version Not Supported";
274  break;
275  case Response::BandwidthLimitExceeded:
276  ret = "HTTP/1.1 509 Bandwidth Limit Exceeded";
277  break;
278  default:
279  ret = QByteArrayLiteral("HTTP/1.1 ").append(QByteArray::number(status)).constData();
280  break;
281  }
282 
283  if (len) {
284  *len = int(strlen(ret));
285  }
286  return ret;
287 }
288 
290 {
291  Q_D(Engine);
292  return d->app->defaultHeaders();
293 }
294 
296 {
297  Q_D(Engine);
298  d->app->handleRequest(request);
299 }
300 
301 QVariantMap Engine::opts() const
302 {
303  Q_D(const Engine);
304  return d->opts;
305 }
306 
307 QVariantMap Engine::config(const QString &entity) const
308 {
309  Q_D(const Engine);
310  return d->config.value(entity).toMap();
311 }
312 
313 void Engine::setConfig(const QVariantMap &config)
314 {
315  Q_D(Engine);
316  d->config = config;
317 }
318 
319 QVariantMap Engine::loadIniConfig(const QString &filename)
320 {
321  QVariantMap ret;
322  QSettings settings(filename, QSettings::IniFormat);
323  if (settings.status() != QSettings::NoError) {
324  qCWarning(CUTELYST_ENGINE) << "Failed to load INI file:" << settings.status();
325  return ret;
326  }
327 
328  const auto groups = settings.childGroups();
329  for (const QString &group : groups) {
330  QVariantMap configGroup;
331  settings.beginGroup(group);
332  const auto child = settings.childKeys();
333  for (const QString &key : child) {
334  configGroup.insert(key, settings.value(key));
335  }
336  settings.endGroup();
337  ret.insert(group, configGroup);
338  }
339 
340  return ret;
341 }
342 
343 QVariantMap Engine::loadJsonConfig(const QString &filename)
344 {
345  QVariantMap ret;
346  QFile file(filename);
347  if (!file.open(QIODevice::ReadOnly)) {
348  return ret;
349  }
351 
352  ret = doc.toVariant().toMap();
353 
354  return ret;
355 }
356 
357 #include "moc_engine.cpp"
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QStringList childKeys() const const
void endGroup()
void setConfig(const QVariantMap &config)
Definition: engine.cpp:313
static const char * httpStatusMessage(quint16 status, int *len=nullptr)
Definition: engine.cpp:148
virtual const QMetaObject * metaObject() const const
void processRequest(EngineRequest *request)
Definition: engine.cpp:295
bool initApplication()
initApplication
Definition: engine.cpp:112
QThread * thread() const const
bool postForkApplication()
postForkApplication
Definition: engine.cpp:129
static QVariantMap loadIniConfig(const QString &filename)
Definition: engine.cpp:319
qint64 currentMSecsSinceEpoch()
QString number(int n, int base)
Engine(Application *app, int workerCore, const QVariantMap &opts)
Definition: engine.cpp:44
QVariantMap opts() const
Definition: engine.cpp:301
void setObjectName(const QString &name)
QVariantMap config(const QString &entity) const
user configuration for the application
Definition: engine.cpp:307
QByteArray number(int n, int base)
QByteArray readAll()
QStringList childGroups() const const
void processRequestAsync(Cutelyst::EngineRequest *request)
static QVariantMap loadJsonConfig(const QString &filename)
Definition: engine.cpp:343
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
virtual bool open(QIODevice::OpenMode mode) override
QVariant toVariant() const const
QSettings::Status status() const const
QVariant value(const QString &key, const QVariant &defaultValue) const const
QMap< QString, QVariant > toMap() const const
Application * app() const
application
Definition: engine.cpp:80
void insert(int i, const T &value)
QThread * currentThread()
virtual quint64 time()
Definition: engine.cpp:143
The Cutelyst Application.
Definition: application.h:42
Headers & defaultHeaders()
Definition: engine.cpp:289
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
The Cutelyst Engine
Definition: engine.h:20
int workerCore() const
Each worker process migth have a number of worker cores (threads), a single process with two worker ...
Definition: engine.cpp:106
QObject * newInstance(QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9) const const
void beginGroup(const QString &prefix)