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