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