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
31Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER, "cutelyst.dispatcher", QtWarningMsg)
32Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_PATH, "cutelyst.dispatcher.path", QtWarningMsg)
33Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_CHAINED, "cutelyst.dispatcher.chained", QtWarningMsg)
34Q_LOGGING_CATEGORY(CUTELYST_CONTROLLER, "cutelyst.controller", QtWarningMsg)
35Q_LOGGING_CATEGORY(CUTELYST_CORE, "cutelyst.core", QtWarningMsg)
36Q_LOGGING_CATEGORY(CUTELYST_ENGINE, "cutelyst.engine", QtWarningMsg)
37Q_LOGGING_CATEGORY(CUTELYST_UPLOAD, "cutelyst.upload", QtWarningMsg)
38Q_LOGGING_CATEGORY(CUTELYST_MULTIPART, "cutelyst.multipart", QtWarningMsg)
39Q_LOGGING_CATEGORY(CUTELYST_VIEW, "cutelyst.view", QtWarningMsg)
40Q_LOGGING_CATEGORY(CUTELYST_REQUEST, "cutelyst.request", QtWarningMsg)
41Q_LOGGING_CATEGORY(CUTELYST_RESPONSE, "cutelyst.response", QtWarningMsg)
42Q_LOGGING_CATEGORY(CUTELYST_STATS, "cutelyst.stats", QtWarningMsg)
43Q_LOGGING_CATEGORY(CUTELYST_COMPONENT, "cutelyst.component", QtWarningMsg)
44
45using 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:"
70 << QCoreApplication::applicationPid();
71 return true;
72}
73
75{
76 qCDebug(CUTELYST_CORE) << "Default Application::postFork called on pid:"
77 << QCoreApplication::applicationPid();
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
137Component *Application::createComponentPlugin(const QString &name, QObject *parent)
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
164const char *Application::cutelystVersion() noexcept
165{
166 return CUTELYST_VERSION;
167}
168
169QVector<Cutelyst::Controller *> Application::controllers() const noexcept
170{
171 Q_D(const Application);
172 return d->controllers;
173}
174
175View *Application::view(QStringView name) const
176{
177 Q_D(const Application);
178 return d->views.value(name);
179}
180
181QVariant 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
197QVector<Cutelyst::DispatchType *> Application::dispatchers() const noexcept
198{
199 Q_D(const Application);
200 return d->dispatcher->dispatchers();
201}
202
203QVector<Plugin *> Application::plugins() const noexcept
204{
205 Q_D(const Application);
206 return d->plugins;
207}
208
209QVariantMap Application::config() const noexcept
210{
211 Q_D(const Application);
212 return d->config;
213}
214
215QString Application::pathTo(const QString &path) const
216{
217 QDir home = config(u"home"_qs).toString();
218 return home.absoluteFilePath(path);
219}
220
221QString Cutelyst::Application::pathTo(const QStringList &path) const
222{
223 QDir home = config(u"home"_qs).toString();
224 return home.absoluteFilePath(path.join(u'/'));
225}
226
227bool 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
239void 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.")
354 .arg(QCoreApplication::applicationName(),
355 QLatin1String(Application::cutelystVersion()),
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
398 Q_EMIT beforeDispatch(c);
399
400 d->dispatcher->dispatch(c);
401
402 if (request->status & EngineRequest::Async) {
403 return;
404 }
405
406 Q_EMIT afterDispatch(c);
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
432void 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
444void Application::addTranslator(const QString &locale, QTranslator *translator)
445{
446 addTranslator(QLocale(locale), translator);
447}
448
449void 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
463static 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
487QString Application::translate(const QLocale &locale,
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
523void Application::loadTranslations(const QString &filename,
524 const QString &directory,
525 const QString &prefix,
526 const QString &suffix)
527{
528 loadTranslationsFromDir(filename, directory, prefix, suffix);
529}
530
531QVector<QLocale> Application::loadTranslationsFromDir(const QString &filename,
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
589QVector<QLocale> Application::loadTranslationsFromDirs(const QString &directory,
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
640QLocale Application::defaultLocale() const noexcept
641{
642 Q_D(const Application);
643 return d->defaultLocale;
644}
645
646void Application::setDefaultLocale(const QLocale &locale)
647{
648 Q_D(Application);
649 d->defaultLocale = locale;
650}
651
652void 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
665void 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
695void 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"
718 << doc.toJson(QJsonDocument::Indented);
719 }
720
721 const auto uploads = req->uploads();
722 if (!uploads.isEmpty()) {
723 logRequestUploads(uploads);
724 }
725}
726
727void 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
745void 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
765Component *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
QMultiMap< QString, QString > ParamsMultiMap
The Cutelyst namespace holds all public Cutelyst API.