cutelyst 5.0.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;
46using 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
165const 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
182QVariant 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
210QVariantMap 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
228bool 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
240void 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
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
433void 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
445void Application::addTranslator(const QString &locale, QTranslator *translator)
446{
447 addTranslator(QLocale(locale), translator);
448}
449
450void 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
464namespace {
465void replacePercentN(QString *result, int n)
466{
467 if (n >= 0) {
468 auto percentPos = 0;
469 auto len = 0;
470 while ((percentPos = result->indexOf(u'%', percentPos + len)) != -1) {
471 len = 1;
472 QString fmt;
473 if (result->at(percentPos + len) == u'L') {
474 ++len;
475 fmt = QStringLiteral("%L1");
476 } else {
477 fmt = QStringLiteral("%1");
478 }
479 if (result->at(percentPos + len) == u'n') {
480 fmt = fmt.arg(n);
481 ++len;
482 result->replace(percentPos, len, fmt);
483 len = fmt.length();
484 }
485 }
486 }
487}
488} // namespace
489
491 const char *context,
492 const char *sourceText,
493 const char *disambiguation,
494 int n) const
495{
496 QString result;
497
498 if (!sourceText) {
499 return result;
500 }
501
502 Q_D(const Application);
503
504 const QVector<QTranslator *> translators = d->translators.value(locale);
505 if (translators.empty()) {
506 result = QString::fromUtf8(sourceText);
507 replacePercentN(&result, n);
508 return result;
509 }
510
511 for (QTranslator *translator : translators) {
512 result = translator->translate(context, sourceText, disambiguation, n);
513 if (!result.isEmpty()) {
514 break;
515 }
516 }
517
518 if (result.isEmpty()) {
519 result = QString::fromUtf8(sourceText);
520 }
521
522 replacePercentN(&result, n);
523 return result;
524}
525
527 const QString &directory,
528 const QString &prefix,
529 const QString &suffix)
530{
531 loadTranslationsFromDir(filename, directory, prefix, suffix);
532}
533
535 const QString &directory,
536 const QString &prefix,
537 const QString &suffix)
538{
539 QVector<QLocale> locales;
540
541 if (Q_LIKELY(!filename.isEmpty())) {
542 const QString _dir = directory.isEmpty() ? QStringLiteral(CUTELYST_I18N_DIR) : directory;
543 const QDir i18nDir(_dir);
544 if (Q_LIKELY(i18nDir.exists())) {
545 const QString _prefix = prefix.isEmpty() ? QStringLiteral(".") : prefix;
546 const QString _suffix = suffix.isEmpty() ? QStringLiteral(".qm") : suffix;
547 const QStringList namesFilter = QStringList({filename + _prefix + u'*' + _suffix});
548
549 const QFileInfoList tsFiles = i18nDir.entryInfoList(namesFilter, QDir::Files);
550 if (Q_LIKELY(!tsFiles.empty())) {
551 locales.reserve(tsFiles.size());
552 for (const QFileInfo &ts : tsFiles) {
553 const QString fn = ts.fileName();
554 const int prefIdx = fn.indexOf(_prefix);
555 const QString locString =
556 fn.mid(prefIdx + _prefix.length(),
557 fn.length() - prefIdx - _suffix.length() - _prefix.length());
558 QLocale loc(locString);
559 if (Q_LIKELY(loc.language() != QLocale::C)) {
560 auto trans = new QTranslator(this);
561 if (Q_LIKELY(trans->load(loc, filename, _prefix, _dir))) {
562 addTranslator(loc, trans);
563 locales.append(loc);
564 qCDebug(CUTELYST_CORE) << "Loaded translations for" << loc << "from"
565 << ts.absoluteFilePath();
566 } else {
567 delete trans;
568 qCWarning(CUTELYST_CORE) << "Can not load translations for" << loc
569 << "from" << ts.absoluteFilePath();
570 }
571 } else {
572 qCWarning(CUTELYST_CORE)
573 << "Can not load translations for invalid locale string" << locString;
574 }
575 }
576 locales.squeeze();
577 } else {
578 qCWarning(CUTELYST_CORE)
579 << "Can not find translation files for" << filename << "in directory" << _dir;
580 }
581 } else {
582 qCWarning(CUTELYST_CORE)
583 << "Can not load translations from not existing directory:" << _dir;
584 }
585 } else {
586 qCWarning(CUTELYST_CORE) << "Can not load translations for empty file name.";
587 }
588
589 return locales;
590}
591
593 const QString &filename)
594{
595 QVector<QLocale> locales;
596
597 if (Q_LIKELY(!directory.isEmpty() && !filename.isEmpty())) {
598 const QDir dir(directory);
599 if (Q_LIKELY(dir.exists())) {
600 const auto dirs = dir.entryList(QDir::AllDirs);
601 if (Q_LIKELY(!dirs.empty())) {
602 locales.reserve(dirs.size());
603 for (const QString &subDir : dirs) {
604 const QString relFn = subDir + u'/' + filename;
605 if (dir.exists(relFn)) {
606 const QLocale l(subDir);
607 if (Q_LIKELY(l.language() != QLocale::C)) {
608 auto trans = new QTranslator(this);
609 const QFileInfo fi(dir, relFn);
610 if (Q_LIKELY(trans->load(
611 l, fi.baseName(), QString(), fi.absolutePath(), fi.suffix()))) {
612 addTranslator(l, trans);
613 locales.append(l);
614 qCDebug(CUTELYST_CORE) << "Loaded translations for" << l << "from"
615 << fi.absoluteFilePath();
616 } else {
617 delete trans;
618 qCWarning(CUTELYST_CORE) << "Can not load translations for" << l
619 << "from" << fi.absoluteFilePath();
620 }
621 } else {
622 qCWarning(CUTELYST_CORE)
623 << "Can not load translations for invalid locale string:" << subDir;
624 }
625 }
626 }
627 locales.squeeze();
628 } else {
629 qCWarning(CUTELYST_CORE) << "Can not find locale dirs under" << directory;
630 }
631 } else {
632 qCWarning(CUTELYST_CORE)
633 << "Can not load translations from not existing directory:" << directory;
634 }
635 } else {
636 qCWarning(CUTELYST_CORE)
637 << "Can not load translations for empty file name or directory name";
638 }
639
640 return locales;
641}
642
644{
645 Q_D(const Application);
646 return d->defaultLocale;
647}
648
650{
651 Q_D(Application);
652 d->defaultLocale = locale;
653}
654
655void Cutelyst::ApplicationPrivate::setupHome()
656{
657 // Hook the current directory in config if "home" is not set
658 if (!config.contains(QLatin1String("home"))) {
659 config.insert(QStringLiteral("home"), QDir::currentPath());
660 }
661
662 if (!config.contains(QLatin1String("root"))) {
663 QDir home = config.value(QLatin1String("home")).toString();
664 config.insert(QStringLiteral("root"), home.absoluteFilePath(QLatin1String("root")));
665 }
666}
667
668void ApplicationPrivate::setupChildren(const QObjectList &children)
669{
670 Q_Q(Application);
671 for (QObject *child : children) {
672 auto controller = qobject_cast<Controller *>(child);
673 if (controller) {
674 q->registerController(controller);
675 continue;
676 }
677
678 auto plugin = qobject_cast<Plugin *>(child);
679 if (plugin) {
680 q->registerPlugin(plugin);
681 continue;
682 }
683
684 auto view = qobject_cast<View *>(child);
685 if (view) {
686 q->registerView(view);
687 continue;
688 }
689
690 auto dispatchType = qobject_cast<DispatchType *>(child);
691 if (dispatchType) {
692 q->registerDispatcher(dispatchType);
693 continue;
694 }
695 }
696}
697
698void Cutelyst::ApplicationPrivate::logRequest(Request *req)
699{
700 QString path = req->path();
701 if (path.isEmpty()) {
702 path = QStringLiteral("/");
703 }
704 qCDebug(CUTELYST_REQUEST) << req->method() << "request for" << path << "from"
705 << req->addressString();
706
707 ParamsMultiMap params = req->queryParameters();
708 if (!params.isEmpty()) {
709 logRequestParameters(params, QLatin1String("Query Parameters are:"));
710 }
711
712 params = req->bodyParameters();
713 if (!params.isEmpty()) {
714 logRequestParameters(params, QLatin1String("Body Parameters are:"));
715 }
716
717 const auto bodyData = req->bodyData();
718 if (bodyData.typeId() == QMetaType::QJsonDocument) {
719 const auto doc = bodyData.toJsonDocument();
720 qCDebug(CUTELYST_REQUEST).noquote() << "JSON body:\n"
722 }
723
724 const auto uploads = req->uploads();
725 if (!uploads.isEmpty()) {
726 logRequestUploads(uploads);
727 }
728}
729
730void Cutelyst::ApplicationPrivate::logRequestParameters(const ParamsMultiMap &params,
731 const QString &title)
732{
734 for (const auto &[key, value] : params.asKeyValueRange()) {
735 table.append({key, value});
736 }
737 qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table,
738 {
739 QLatin1String("Parameter"),
740 QLatin1String("Value"),
741 },
742 title)
743 .constData();
744}
745
746void Cutelyst::ApplicationPrivate::logRequestUploads(const QVector<Cutelyst::Upload *> &uploads)
747{
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
766Component *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"
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)
QVector< QLocale > loadTranslationsFromDir(const QString &filename, const QString &directory={}, const QString &prefix=QStringLiteral("."), const QString &suffix=QStringLiteral(".qm"))
void handleRequest(Cutelyst::EngineRequest *request)
void beforePrepareAction(Cutelyst::Context *c, bool *skipMethod)
Application(QObject *parent=nullptr)
void setConfig(const QString &key, const QVariant &value)
QVariantMap config() const noexcept
Dispatcher * dispatcher() const noexcept
bool registerPlugin(Plugin *plugin)
bool registerController(Controller *controller)
bool registerDispatcher(DispatchType *dispatcher)
QString pathTo(const QString &path) const
Headers & defaultHeaders() noexcept
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)
Component * createComponentPlugin(const QString &name, QObject *parent=nullptr)
void loadTranslations(const QString &filename, const QString &directory={}, const QString &prefix={}, const QString &suffix={})
virtual bool init()
void postForked(Cutelyst::Application *app)
virtual bool postFork()
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.
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:122
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:40
QVector< Upload * > uploads() const
Definition request.cpp:379
ParamsMultiMap bodyParameters() const
Definition request.cpp:220
ParamsMultiMap queryParameters() const
Definition request.cpp:256
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
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