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