cutelyst  4.3.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
application.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2013-2022 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "application_p.h"
6 #include "common.h"
7 #include "config.h"
8 #include "context_p.h"
9 #include "controller.h"
10 #include "controller_p.h"
11 #include "dispatchtype.h"
12 #include "enginerequest.h"
13 #include "request.h"
14 #include "request_p.h"
15 #include "response.h"
16 #include "response_p.h"
17 #include "stats.h"
18 #include "utils.h"
19 #include "view.h"
20 
21 #include <QJsonDocument>
22 #include <QtCore/QCoreApplication>
23 #include <QtCore/QDataStream>
24 #include <QtCore/QDir>
25 #include <QtCore/QFileInfo>
26 #include <QtCore/QLocale>
27 #include <QtCore/QPluginLoader>
28 #include <QtCore/QStringList>
29 #include <QtCore/QTranslator>
30 
31 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER, "cutelyst.dispatcher", QtWarningMsg)
32 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_PATH, "cutelyst.dispatcher.path", QtWarningMsg)
33 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_CHAINED, "cutelyst.dispatcher.chained", QtWarningMsg)
34 Q_LOGGING_CATEGORY(CUTELYST_CONTROLLER, "cutelyst.controller", QtWarningMsg)
35 Q_LOGGING_CATEGORY(CUTELYST_CORE, "cutelyst.core", QtWarningMsg)
36 Q_LOGGING_CATEGORY(CUTELYST_ENGINE, "cutelyst.engine", QtWarningMsg)
37 Q_LOGGING_CATEGORY(CUTELYST_UPLOAD, "cutelyst.upload", QtWarningMsg)
38 Q_LOGGING_CATEGORY(CUTELYST_MULTIPART, "cutelyst.multipart", QtWarningMsg)
39 Q_LOGGING_CATEGORY(CUTELYST_VIEW, "cutelyst.view", QtWarningMsg)
40 Q_LOGGING_CATEGORY(CUTELYST_REQUEST, "cutelyst.request", QtWarningMsg)
41 Q_LOGGING_CATEGORY(CUTELYST_RESPONSE, "cutelyst.response", QtWarningMsg)
42 Q_LOGGING_CATEGORY(CUTELYST_STATS, "cutelyst.stats", QtWarningMsg)
43 Q_LOGGING_CATEGORY(CUTELYST_COMPONENT, "cutelyst.component", QtWarningMsg)
44 
45 using namespace Cutelyst;
46 
48  : QObject(parent)
49  , d_ptr(new ApplicationPrivate)
50 {
51  Q_D(Application);
52 
53  d->q_ptr = this;
54 
55  qRegisterMetaType<ParamsMultiMap>();
56 
57  d->dispatcher = new Dispatcher(this);
58 
59  loadTranslations(QStringLiteral("cutelystcore"));
60 }
61 
63 {
64  delete d_ptr;
65 }
66 
68 {
69  qCDebug(CUTELYST_CORE) << "Default Application::init called on pid:"
71  return true;
72 }
73 
75 {
76  qCDebug(CUTELYST_CORE) << "Default Application::postFork called on pid:"
78  return true;
79 }
80 
82 {
83  Q_D(Application);
84  return d->headers;
85 }
86 
88 {
89  Q_D(Application);
90  d->headers.setHeader("X-Cutelyst"_qba, QByteArrayLiteral(CUTELYST_VERSION));
91 }
92 
94 {
95  Q_D(Application);
96  if (d->plugins.contains(plugin)) {
97  return false;
98  }
99  d->plugins.append(plugin);
100  return true;
101 }
102 
104 {
105  Q_D(Application);
106  const auto name = QString::fromLatin1(controller->metaObject()->className());
107  if (d->controllersHash.contains(name)) {
108  return false;
109  }
110  d->controllersHash.insert(name, controller);
111  d->controllers.append(controller);
112  return true;
113 }
114 
116 {
117  Q_D(Application);
118  if (d->views.contains(view->name())) {
119  qCWarning(CUTELYST_CORE) << "Not registering View." << view->metaObject()->className()
120  << "There is already a view with this name:" << view->name();
121  return false;
122  }
123  d->views.insert(view->name(), view);
124  return true;
125 }
126 
128 {
129  Q_D(Application);
130  if (d->dispatchers.contains(dispatcher)) {
131  return false;
132  }
133  d->dispatchers.append(dispatcher);
134  return true;
135 }
136 
138 {
139  Q_D(Application);
140  auto it = d->factories.constFind(name);
141  if (it != d->factories.constEnd()) {
142  ComponentFactory *factory = it.value();
143  if (factory) {
144  return factory->createComponent(parent);
145  } else {
146  return nullptr;
147  }
148  }
149 
150  const QByteArrayList dirs = QByteArrayList{QByteArrayLiteral(CUTELYST_PLUGINS_DIR)} +
151  qgetenv("CUTELYST_PLUGINS_DIR").split(';');
152  for (const QByteArray &dir : dirs) {
153  Component *component = d->createComponentPlugin(name, parent, QString::fromLocal8Bit(dir));
154  if (component) {
155  return component;
156  }
157  }
158  qCDebug(CUTELYST_CORE) << "Did not find plugin" << name << "on" << dirs << "for" << parent;
159 
160  return nullptr;
161 }
162 
163 const char *Application::cutelystVersion() noexcept
164 {
165  return CUTELYST_VERSION;
166 }
167 
169 {
170  Q_D(const Application);
171  return d->controllers;
172 }
173 
175 {
176  Q_D(const Application);
177  return d->views.value(name);
178 }
179 
180 QVariant Application::config(const QString &key, const QVariant &defaultValue) const
181 {
182  Q_D(const Application);
183  auto it = d->config.constFind(key);
184  if (it != d->config.constEnd()) {
185  return it.value();
186  }
187  return defaultValue;
188 }
189 
191 {
192  Q_D(const Application);
193  return d->dispatcher;
194 }
195 
197 {
198  Q_D(const Application);
199  return d->dispatcher->dispatchers();
200 }
201 
203 {
204  Q_D(const Application);
205  return d->plugins;
206 }
207 
208 QVariantMap Application::config() const noexcept
209 {
210  Q_D(const Application);
211  return d->config;
212 }
213 
215 {
216  QDir home = config(u"home"_qs).toString();
217  return home.absoluteFilePath(path);
218 }
219 
221 {
222  QDir home = config(u"home"_qs).toString();
223  return home.absoluteFilePath(path.join(u'/'));
224 }
225 
226 bool Cutelyst::Application::inited() const noexcept
227 {
228  Q_D(const Application);
229  return d->init;
230 }
231 
233 {
234  Q_D(const Application);
235  return d->engine;
236 }
237 
238 void Application::setConfig(const QString &key, const QVariant &value)
239 {
240  Q_D(Application);
241  d->config.insert(key, value);
242 }
243 
245 {
246  Q_D(Application);
247 
248  if (d->init) {
249  return true;
250  }
251  d->init = true;
252 
253  d->useStats = CUTELYST_STATS().isDebugEnabled();
254  d->engine = engine;
255  d->config = engine->config(QLatin1String("Cutelyst"));
256 
257  d->setupHome();
258 
259  // Call the virtual application init
260  // to setup Controllers plugins stuff
261  if (init()) {
262  d->setupChildren(children());
263 
264  bool zeroCore = engine->workerCore() == 0;
265 
266  QVector<QStringList> tablePlugins;
267  const auto plugins = d->plugins;
268  for (Plugin *plugin : plugins) {
269  if (plugin->objectName().isEmpty()) {
270  plugin->setObjectName(QString::fromLatin1(plugin->metaObject()->className()));
271  }
272  tablePlugins.append({plugin->objectName()});
273  // Configure plugins
274  plugin->setup(this);
275  }
276 
277  if (zeroCore && !tablePlugins.isEmpty()) {
278  qCDebug(CUTELYST_CORE)
279  << Utils::buildTable(tablePlugins, QStringList(), QLatin1String("Loaded plugins:"))
280  .constData();
281  }
282 
283  if (zeroCore) {
284  QVector<QStringList> tableDataHandlers;
285  tableDataHandlers.append({u"application/x-www-form-urlencoded"_qs});
286  tableDataHandlers.append({u"application/json"_qs});
287  tableDataHandlers.append({u"multipart/form-data"_qs});
288  qCDebug(CUTELYST_CORE)
289  << Utils::buildTable(tableDataHandlers,
290  QStringList(),
291  QLatin1String("Loaded Request Data Handlers:"))
292  .constData();
293 
294  qCDebug(CUTELYST_CORE) << "Loaded dispatcher"
295  << QString::fromLatin1(d->dispatcher->metaObject()->className());
296  qCDebug(CUTELYST_CORE)
297  << "Using engine" << QString::fromLatin1(d->engine->metaObject()->className());
298  }
299 
300  QString home = d->config.value(u"home"_qs).toString();
301  if (home.isEmpty()) {
302  if (zeroCore) {
303  qCDebug(CUTELYST_CORE) << "Couldn't find home";
304  }
305  } else {
306  QFileInfo homeInfo(home);
307  if (homeInfo.isDir()) {
308  if (zeroCore) {
309  qCDebug(CUTELYST_CORE) << "Found home" << home;
310  }
311  } else {
312  if (zeroCore) {
313  qCDebug(CUTELYST_CORE) << "Home" << home << "doesn't exist";
314  }
315  }
316  }
317 
318  QVector<QStringList> table;
319  QStringList controllerNames = d->controllersHash.keys();
320  controllerNames.sort();
321  for (const QString &controller : controllerNames) {
322  table.append({controller, QLatin1String("Controller")});
323  }
324 
325  const auto views = d->views;
326  for (View *view : views) {
327  if (view->reverse().isEmpty()) {
328  const QString className = QString::fromLatin1(view->metaObject()->className()) +
329  QLatin1String("->execute");
330  view->setReverse(className);
331  }
332  table.append({view->reverse(), QLatin1String("View")});
333  }
334 
335  if (zeroCore && !table.isEmpty()) {
336  qCDebug(CUTELYST_CORE)
337  << Utils::buildTable(table,
338  {QLatin1String("Class"), QLatin1String("Type")},
339  QLatin1String("Loaded components:"))
340  .constData();
341  }
342 
343  const auto controllers = d->controllers;
344  for (Controller *controller : controllers) {
345  controller->d_ptr->init(this, d->dispatcher);
346  }
347 
348  d->dispatcher->setupActions(d->controllers, d->dispatchers, d->engine->workerCore() == 0);
349 
350  if (zeroCore) {
351  qCInfo(CUTELYST_CORE) << qPrintable(
352  QString::fromLatin1("%1 powered by Cutelyst %2, Qt %3.")
355  QLatin1String(qVersion())));
356  }
357 
358  Q_EMIT preForked(this);
359 
360  return true;
361  }
362 
363  return false;
364 }
365 
367 {
368  Q_D(Application);
369 
370  Engine *engine = d->engine;
371 
372  auto priv = new ContextPrivate(this, engine, d->dispatcher, d->plugins);
373  auto c = new Context(priv);
374 
375  request->context = c;
376  priv->engineRequest = request;
377  priv->response = new Response(d->headers, request);
378  priv->request = new Request(request);
379  priv->locale = d->defaultLocale;
380 
381  if (d->useStats) {
382  priv->stats = new Stats(request);
383  }
384 
385  // Process request
386  bool skipMethod = false;
387  Q_EMIT beforePrepareAction(c, &skipMethod);
388 
389  if (!skipMethod) {
390  static bool log = CUTELYST_REQUEST().isEnabled(QtDebugMsg);
391  if (log) {
392  d->logRequest(priv->request);
393  }
394 
395  d->dispatcher->prepareAction(c);
396 
398 
399  d->dispatcher->dispatch(c);
400 
401  if (request->status & EngineRequest::Async) {
402  return;
403  }
404 
406  }
407 
408  c->finalize();
409 }
410 
412 {
413  Q_D(Application);
414 
415  if (!postFork()) {
416  return false;
417  }
418 
419  const auto controllers = d->controllers;
420  for (Controller *controller : controllers) {
421  if (!controller->postFork(this)) {
422  return false;
423  }
424  }
425 
426  Q_EMIT postForked(this);
427 
428  return true;
429 }
430 
431 void Application::addTranslator(const QLocale &locale, QTranslator *translator)
432 {
433  Q_D(Application);
434  Q_ASSERT_X(translator, "add translator to application", "invalid QTranslator object");
435  auto it = d->translators.find(locale);
436  if (it != d->translators.end()) {
437  it.value().prepend(translator);
438  } else {
439  d->translators.insert(locale, QVector<QTranslator *>(1, translator));
440  }
441 }
442 
443 void Application::addTranslator(const QString &locale, QTranslator *translator)
444 {
445  addTranslator(QLocale(locale), translator);
446 }
447 
448 void Application::addTranslators(const QLocale &locale, const QVector<QTranslator *> &translators)
449 {
450  Q_D(Application);
451  Q_ASSERT_X(!translators.empty(), "add translators to application", "empty translators vector");
452  auto transIt = d->translators.find(locale);
453  if (transIt != d->translators.end()) {
454  for (auto it = translators.crbegin(); it != translators.crend(); ++it) {
455  transIt.value().prepend(*it);
456  }
457  } else {
458  d->translators.insert(locale, translators);
459  }
460 }
461 
462 static void replacePercentN(QString *result, int n)
463 {
464  if (n >= 0) {
465  auto percentPos = 0;
466  auto len = 0;
467  while ((percentPos = result->indexOf(u'%', percentPos + len)) != -1) {
468  len = 1;
469  QString fmt;
470  if (result->at(percentPos + len) == u'L') {
471  ++len;
472  fmt = QStringLiteral("%L1");
473  } else {
474  fmt = QStringLiteral("%1");
475  }
476  if (result->at(percentPos + len) == u'n') {
477  fmt = fmt.arg(n);
478  ++len;
479  result->replace(percentPos, len, fmt);
480  len = fmt.length();
481  }
482  }
483  }
484 }
485 
487  const char *context,
488  const char *sourceText,
489  const char *disambiguation,
490  int n) const
491 {
492  QString result;
493 
494  if (!sourceText) {
495  return result;
496  }
497 
498  Q_D(const Application);
499 
500  const QVector<QTranslator *> translators = d->translators.value(locale);
501  if (translators.empty()) {
502  result = QString::fromUtf8(sourceText);
503  replacePercentN(&result, n);
504  return result;
505  }
506 
507  for (QTranslator *translator : translators) {
508  result = translator->translate(context, sourceText, disambiguation, n);
509  if (!result.isEmpty()) {
510  break;
511  }
512  }
513 
514  if (result.isEmpty()) {
515  result = QString::fromUtf8(sourceText);
516  }
517 
518  replacePercentN(&result, n);
519  return result;
520 }
521 
523  const QString &directory,
524  const QString &prefix,
525  const QString &suffix)
526 {
527  loadTranslationsFromDir(filename, directory, prefix, suffix);
528 }
529 
531  const QString &directory,
532  const QString &prefix,
533  const QString &suffix)
534 {
535  QVector<QLocale> locales;
536 
537  if (Q_LIKELY(!filename.isEmpty())) {
538  const QString _dir = directory.isEmpty() ? QStringLiteral(CUTELYST_I18N_DIR) : directory;
539  const QDir i18nDir(_dir);
540  if (Q_LIKELY(i18nDir.exists())) {
541  const QString _prefix = prefix.isEmpty() ? QStringLiteral(".") : prefix;
542  const QString _suffix = suffix.isEmpty() ? QStringLiteral(".qm") : suffix;
543  const QStringList namesFilter = QStringList({filename + _prefix + u'*' + _suffix});
544 
545  const QFileInfoList tsFiles = i18nDir.entryInfoList(namesFilter, QDir::Files);
546  if (Q_LIKELY(!tsFiles.empty())) {
547  locales.reserve(tsFiles.size());
548  for (const QFileInfo &ts : tsFiles) {
549  const QString fn = ts.fileName();
550  const int prefIdx = fn.indexOf(_prefix);
551  const QString locString =
552  fn.mid(prefIdx + _prefix.length(),
553  fn.length() - prefIdx - _suffix.length() - _prefix.length());
554  QLocale loc(locString);
555  if (Q_LIKELY(loc.language() != QLocale::C)) {
556  auto trans = new QTranslator(this);
557  if (Q_LIKELY(trans->load(loc, filename, _prefix, _dir))) {
558  addTranslator(loc, trans);
559  locales.append(loc);
560  qCDebug(CUTELYST_CORE) << "Loaded translations for" << loc << "from"
561  << ts.absoluteFilePath();
562  } else {
563  delete trans;
564  qCWarning(CUTELYST_CORE) << "Can not load translations for" << loc
565  << "from" << ts.absoluteFilePath();
566  }
567  } else {
568  qCWarning(CUTELYST_CORE)
569  << "Can not load translations for invalid locale string" << locString;
570  }
571  }
572  locales.squeeze();
573  } else {
574  qCWarning(CUTELYST_CORE)
575  << "Can not find translation files for" << filename << "in directory" << _dir;
576  }
577  } else {
578  qCWarning(CUTELYST_CORE)
579  << "Can not load translations from not existing directory:" << _dir;
580  }
581  } else {
582  qCWarning(CUTELYST_CORE) << "Can not load translations for empty file name.";
583  }
584 
585  return locales;
586 }
587 
589  const QString &filename)
590 {
591  QVector<QLocale> locales;
592 
593  if (Q_LIKELY(!directory.isEmpty() && !filename.isEmpty())) {
594  const QDir dir(directory);
595  if (Q_LIKELY(dir.exists())) {
596  const auto dirs = dir.entryList(QDir::AllDirs);
597  if (Q_LIKELY(!dirs.empty())) {
598  locales.reserve(dirs.size());
599  for (const QString &subDir : dirs) {
600  const QString relFn = subDir + u'/' + filename;
601  if (dir.exists(relFn)) {
602  const QLocale l(subDir);
603  if (Q_LIKELY(l.language() != QLocale::C)) {
604  auto trans = new QTranslator(this);
605  const QFileInfo fi(dir, relFn);
606  if (Q_LIKELY(trans->load(
607  l, fi.baseName(), QString(), fi.absolutePath(), fi.suffix()))) {
608  addTranslator(l, trans);
609  locales.append(l);
610  qCDebug(CUTELYST_CORE) << "Loaded translations for" << l << "from"
611  << fi.absoluteFilePath();
612  } else {
613  delete trans;
614  qCWarning(CUTELYST_CORE) << "Can not load translations for" << l
615  << "from" << fi.absoluteFilePath();
616  }
617  } else {
618  qCWarning(CUTELYST_CORE)
619  << "Can not load translations for invalid locale string:" << subDir;
620  }
621  }
622  }
623  locales.squeeze();
624  } else {
625  qCWarning(CUTELYST_CORE) << "Can not find locale dirs under" << directory;
626  }
627  } else {
628  qCWarning(CUTELYST_CORE)
629  << "Can not load translations from not existing directory:" << directory;
630  }
631  } else {
632  qCWarning(CUTELYST_CORE)
633  << "Can not load translations for empty file name or directory name";
634  }
635 
636  return locales;
637 }
638 
640 {
641  Q_D(const Application);
642  return d->defaultLocale;
643 }
644 
646 {
647  Q_D(Application);
648  d->defaultLocale = locale;
649 }
650 
651 void Cutelyst::ApplicationPrivate::setupHome()
652 {
653  // Hook the current directory in config if "home" is not set
654  if (!config.contains(QLatin1String("home"))) {
655  config.insert(QStringLiteral("home"), QDir::currentPath());
656  }
657 
658  if (!config.contains(QLatin1String("root"))) {
659  QDir home = config.value(QLatin1String("home")).toString();
660  config.insert(QStringLiteral("root"), home.absoluteFilePath(QLatin1String("root")));
661  }
662 }
663 
664 void ApplicationPrivate::setupChildren(const QObjectList &children)
665 {
666  Q_Q(Application);
667  for (QObject *child : children) {
668  auto controller = qobject_cast<Controller *>(child);
669  if (controller) {
670  q->registerController(controller);
671  continue;
672  }
673 
674  auto plugin = qobject_cast<Plugin *>(child);
675  if (plugin) {
676  q->registerPlugin(plugin);
677  continue;
678  }
679 
680  auto view = qobject_cast<View *>(child);
681  if (view) {
682  q->registerView(view);
683  continue;
684  }
685 
686  auto dispatchType = qobject_cast<DispatchType *>(child);
687  if (dispatchType) {
688  q->registerDispatcher(dispatchType);
689  continue;
690  }
691  }
692 }
693 
694 void Cutelyst::ApplicationPrivate::logRequest(Request *req)
695 {
696  QString path = req->path();
697  if (path.isEmpty()) {
698  path = QStringLiteral("/");
699  }
700  qCDebug(CUTELYST_REQUEST) << req->method() << "request for" << path << "from"
701  << req->addressString();
702 
703  ParamsMultiMap params = req->queryParameters();
704  if (!params.isEmpty()) {
705  logRequestParameters(params, QLatin1String("Query Parameters are:"));
706  }
707 
708  params = req->bodyParameters();
709  if (!params.isEmpty()) {
710  logRequestParameters(params, QLatin1String("Body Parameters are:"));
711  }
712 
713  const auto bodyData = req->bodyData();
714  if (bodyData.typeId() == QMetaType::QJsonDocument) {
715  const auto doc = bodyData.toJsonDocument();
716  qCDebug(CUTELYST_REQUEST).noquote() << "JSON body:\n"
718  }
719 
720  const auto uploads = req->uploads();
721  if (!uploads.isEmpty()) {
722  logRequestUploads(uploads);
723  }
724 }
725 
726 void Cutelyst::ApplicationPrivate::logRequestParameters(const ParamsMultiMap &params,
727  const QString &title)
728 {
729  QVector<QStringList> table;
730  auto it = params.constBegin();
731  while (it != params.constEnd()) {
732  table.append({it.key(), it.value()});
733  ++it;
734  }
735  qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table,
736  {
737  QLatin1String("Parameter"),
738  QLatin1String("Value"),
739  },
740  title)
741  .constData();
742 }
743 
744 void Cutelyst::ApplicationPrivate::logRequestUploads(const QVector<Cutelyst::Upload *> &uploads)
745 {
746  QVector<QStringList> table;
747  for (Upload *upload : uploads) {
748  table.append({upload->name(),
749  upload->filename(),
750  QString::fromLatin1(upload->contentType()),
751  QString::number(upload->size())});
752  }
753  qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table,
754  {
755  QLatin1String("Parameter"),
756  QLatin1String("Filename"),
757  QLatin1String("Type"),
758  QLatin1String("Size"),
759  },
760  QLatin1String("File Uploads are:"))
761  .constData();
762 }
763 
764 Component *ApplicationPrivate::createComponentPlugin(const QString &name,
765  QObject *parent,
766  const QString &directory)
767 {
768  Component *component = nullptr;
769 
770  QDir pluginsDir(directory);
771  QPluginLoader loader;
772  ComponentFactory *factory = nullptr;
773  const auto plugins = pluginsDir.entryList(QDir::Files);
774  for (const QString &fileName : plugins) {
775  loader.setFileName(pluginsDir.absoluteFilePath(fileName));
776  const QJsonObject json = loader.metaData()[QLatin1String("MetaData")].toObject();
777  if (json[QLatin1String("name")].toString() == name) {
778  QObject *plugin = loader.instance();
779  if (plugin) {
780  factory = qobject_cast<ComponentFactory *>(plugin);
781  if (!factory) {
782  qCCritical(CUTELYST_CORE)
783  << "Could not create a factory for" << loader.fileName();
784  } else {
785  component = factory->createComponent(parent);
786  }
787  break;
788  } else {
789  qCCritical(CUTELYST_CORE)
790  << "Could not load plugin" << loader.fileName() << loader.errorString();
791  }
792  }
793  }
794 
795  if (factory) {
796  factories.insert(name, factory);
797  }
798 
799  return component;
800 }
801 
802 #include "moc_application.cpp"
void setDefaultLocale(const QLocale &locale)
QVector< DispatchType * > dispatchers() const noexcept
void setConfig(const QString &key, const QVariant &value)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
QString & append(QChar ch)
QVariantMap config() const noexcept
void postForked(Cutelyst::Application *app)
QByteArray toJson(QJsonDocument::JsonFormat format) const const
QJsonDocument toJsonDocument() const const
void setReverse(const QString &reverse)
Definition: component.cpp:51
void reserve(qsizetype size)
QMultiMap::const_iterator constEnd() const const
bool isEmpty() const const
const QObjectList & children() const const
Container for HTTP headers.
Definition: headers.h:23
virtual const QMetaObject * metaObject() const const
bool registerView(View *view)
QVector< Controller * > controllers() const noexcept
QString join(QStringView separator) const const
QString fromUtf8(QByteArrayView str)
QString currentPath()
QJsonObject metaData() const const
QString reverse() const noexcept
Definition: component.cpp:45
void setFileName(const QString &fileName)
void addXCutelystVersionHeader()
Definition: application.cpp:87
bool setup(Engine *engine)
void addTranslators(const QLocale &locale, const QVector< QTranslator *> &translators)
void preForked(Cutelyst::Application *app)
The Cutelyst Component base class.
Definition: component.h:30
void handleRequest(Cutelyst::EngineRequest *request)
T value(qsizetype i) const const
Headers & defaultHeaders() noexcept
Definition: application.cpp:81
A Cutelyst response.
Definition: response.h:28
Cutelyst Upload handles file upload requests.
Definition: upload.h:25
QMultiMap::const_iterator constBegin() const const
ParamsMultiMap bodyParameters() const
Definition: request.cpp:219
The Cutelyst Context.
Definition: context.h:42
QString number(int n, int base)
bool exists() const const
Cutelyst Controller base class.
Definition: controller.h:55
QVector< Upload * > uploads() const
Definition: request.cpp:378
void append(QList::parameter_type value)
QString & insert(qsizetype position, QChar ch)
bool registerController(Controller *controller)
QString addressString() const
Definition: request.cpp:39
QList::const_reverse_iterator crbegin() const const
bool empty() const const
QString fromLocal8Bit(QByteArrayView str)
QLocale defaultLocale() const noexcept
bool isDir() const const
virtual bool init()
Definition: application.cpp:67
Component * createComponentPlugin(const QString &name, QObject *parent=nullptr)
QVector< Plugin * > plugins() const noexcept
void beforePrepareAction(Cutelyst::Context *c, bool *skipMethod)
bool isEmpty() const const
QFileInfoList entryInfoList(QDir::Filters filters, QDir::SortFlags sort) const const
QVariantMap config(const QString &entity) const
Definition: engine.cpp:263
QString absoluteFilePath() const const
bool isEmpty() const const
QObject * instance()
void addTranslator(const QLocale &locale, QTranslator *translator)
QString name() const noexcept
Definition: component.cpp:33
QLocale::Language language() const const
QString pathTo(const QString &path) const
qint64 applicationPid()
QString errorString() const const
The Cutelyst namespace holds all public Cutelyst API.
View * view(QStringView name={}) const
bool registerDispatcher(DispatchType *dispatcher)
const char * className() const const
void beforeDispatch(Cutelyst::Context *c)
ParamsMultiMap queryParameters() const
Definition: request.cpp:255
Application(QObject *parent=nullptr)
Definition: application.cpp:47
QString & replace(qsizetype position, qsizetype n, QChar after)
QString fromLatin1(QByteArrayView str)
virtual bool postFork()
Definition: application.cpp:74
void afterDispatch(Cutelyst::Context *c)
QString mid(qsizetype position, qsizetype n) const const
QString suffix() const const
Dispatcher * dispatcher() const noexcept
bool inited() const noexcept
Abstract class to described a dispatch type.
Definition: dispatchtype.h:24
QStringList entryList(QDir::Filters filters, QDir::SortFlags sort) const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
const QChar at(qsizetype position) const const
Abstract View component for Cutelyst.
Definition: view.h:24
QString absoluteFilePath(const QString &fileName) const const
qsizetype length() const const
Base class for Cutelyst Plugins.
Definition: plugin.h:24
void sort(Qt::CaseSensitivity cs)
The Cutelyst application.
Definition: application.h:72
Engine * engine() const noexcept
A request.
Definition: request.h:41
bool registerPlugin(Plugin *plugin)
Definition: application.cpp:93
void squeeze()
QString absolutePath() const const
virtual Component * createComponent(QObject *parent=nullptr)=0
QObject * parent() const const
QVector< QLocale > loadTranslationsFromDir(const QString &filename, const QString &directory=QString(), const QString &prefix=QStringLiteral("."), const QString &suffix=QStringLiteral(".qm"))
The Cutelyst Engine.
Definition: engine.h:19
QVector< QLocale > loadTranslationsFromDirs(const QString &directory, const QString &filename)
The Cutelyst Dispatcher.
Definition: dispatcher.h:28
int workerCore() const
Definition: engine.cpp:67
QList::const_reverse_iterator crend() const const
QString translate(const QLocale &locale, const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
QString baseName() const const
Q_EMITQ_EMIT
QString applicationName()
static const char * cutelystVersion() noexcept
void loadTranslations(const QString &filename, const QString &directory={}, const QString &prefix={}, const QString &suffix={})