5#include "application_p.h"
10#include "controller_p.h"
11#include "dispatchtype.h"
16#include "response_p.h"
21#include <QtCore/QCoreApplication>
22#include <QtCore/QDataStream>
24#include <QtCore/QFileInfo>
25#include <QtCore/QLocale>
26#include <QtCore/QPluginLoader>
27#include <QtCore/QStringList>
28#include <QtCore/QTranslator>
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)
48 , d_ptr(new ApplicationPrivate)
54 qRegisterMetaType<ParamsMultiMap>();
55#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
56 qRegisterMetaTypeStreamOperators<ParamsMultiMap>(
"ParamsMultiMap");
64Application::~Application()
71 qCDebug(CUTELYST_CORE) <<
"Default Application::init called on pid:"
78 qCDebug(CUTELYST_CORE) <<
"Default Application::postFork called on pid:"
92 d->headers.setHeader(QStringLiteral(
"X_CUTELYST"), QStringLiteral(VERSION));
98 if (d->plugins.contains(
plugin)) {
101 d->plugins.append(
plugin);
109 if (d->controllersHash.contains(name)) {
112 d->controllersHash.insert(name, controller);
113 d->controllers.append(controller);
120 if (d->views.contains(
view->name())) {
121 qCWarning(CUTELYST_CORE) <<
"Not registering View." <<
view->metaObject()->className()
122 <<
"There is already a view with this name:" <<
view->name();
125 d->views.insert(
view->name(),
view);
142 auto it = d->factories.constFind(name);
143 if (it != d->factories.constEnd()) {
153 qgetenv(
"CUTELYST_PLUGINS_DIR").split(
';');
160 qCDebug(CUTELYST_CORE) <<
"Did not find plugin" << name <<
"on" << dirs <<
"for" <<
parent;
173 return d->controllers;
179 return d->views.value(name);
185 return d->views.value(name);
191 auto it = d->config.constFind(key);
192 if (it != d->config.constEnd()) {
201 return d->dispatcher;
207 return d->dispatcher->dispatchers();
224 QDir home =
config(QStringLiteral(
"home")).toString();
230 QDir home =
config(QStringLiteral(
"home")).toString();
249 d->config.insert(key, value);
261 d->useStats = CUTELYST_STATS().isDebugEnabled();
272 bool zeroCore =
engine->workerCore() == 0;
275 const auto plugins = d->plugins;
277 if (
plugin->objectName().isEmpty()) {
285 if (zeroCore && !tablePlugins.
isEmpty()) {
286 qCDebug(CUTELYST_CORE)
296 qCDebug(CUTELYST_CORE)
297 << Utils::buildTable(tableDataHandlers,
302 qCDebug(CUTELYST_CORE) <<
"Loaded dispatcher"
304 qCDebug(CUTELYST_CORE)
311 qCDebug(CUTELYST_CORE) <<
"Couldn't find home";
315 if (homeInfo.
isDir()) {
317 qCDebug(CUTELYST_CORE) <<
"Found home" << home;
321 qCDebug(CUTELYST_CORE) <<
"Home" << home <<
"doesn't exist";
327 QStringList controllerNames = d->controllersHash.keys();
328 controllerNames.
sort();
329 for (
const QString &controller : controllerNames) {
333 const auto views = d->views;
335 if (
view->reverse().isEmpty()) {
338 view->setReverse(className);
343 if (zeroCore && !table.
isEmpty()) {
344 qCDebug(CUTELYST_CORE)
345 << Utils::buildTable(table,
353 controller->d_ptr->init(
this, d->dispatcher);
356 d->dispatcher->setupActions(d->controllers, d->dispatchers, d->engine->workerCore() == 0);
359 qCInfo(CUTELYST_CORE) << qPrintable(
378 Engine *
engine = d->engine;
380 auto priv =
new ContextPrivate(
this,
engine, d->dispatcher, d->plugins);
381 auto c =
new Context(priv);
384 priv->engineRequest = request;
385 priv->response =
new Response(d->headers, request);
386 priv->request =
new Request(request);
389 priv->stats =
new Stats(request);
393 bool skipMethod =
false;
397 static bool log = CUTELYST_REQUEST().isEnabled(QtDebugMsg);
399 d->logRequest(priv->request);
402 d->dispatcher->prepareAction(c);
406 d->dispatcher->dispatch(c);
408 if (request->
status & EngineRequest::Async) {
428 if (!controller->postFork(
this)) {
441 Q_ASSERT_X(translator,
"add translator to application",
"invalid QTranslator object");
442 auto it = d->translators.find(locale);
443 if (it != d->translators.end()) {
444 it.value().prepend(translator);
458 Q_ASSERT_X(!translators.
empty(),
"add translators to application",
"empty translators vector");
459 auto transIt = d->translators.find(locale);
460 if (transIt != d->translators.end()) {
461 for (
auto it = translators.
crbegin(); it != translators.
crend(); ++it) {
462 transIt.value().prepend(*it);
465 d->translators.insert(locale, translators);
469static void replacePercentN(
QString *result,
int n)
474 while ((percentPos = result->
indexOf(u
'%', percentPos + len)) != -1) {
477 if (result->
at(percentPos + len) == u
'L') {
479 fmt = QStringLiteral(
"%L1");
481 fmt = QStringLiteral(
"%1");
483 if (result->
at(percentPos + len) == u
'n') {
486 result->
replace(percentPos, len, fmt);
495 const char *sourceText,
496 const char *disambiguation,
508 if (translators.
empty()) {
510 replacePercentN(&result, n);
515 result = translator->translate(context, sourceText, disambiguation, n);
525 replacePercentN(&result, n);
544 if (Q_LIKELY(!filename.
isEmpty())) {
545 const QString _dir = directory.
isEmpty() ? QStringLiteral(I18NDIR) : directory;
546 const QDir i18nDir(_dir);
547 if (Q_LIKELY(i18nDir.
exists())) {
548 const QString _prefix = prefix.
isEmpty() ? QStringLiteral(
".") : prefix;
549 const QString _suffix = suffix.
isEmpty() ? QStringLiteral(
".qm") : suffix;
553 if (Q_LIKELY(!tsFiles.empty())) {
554 locales.
reserve(tsFiles.size());
556 const QString fn = ts.fileName();
557 const int prefIdx = fn.
indexOf(_prefix);
564 if (Q_LIKELY(trans->load(loc, filename, _prefix, _dir))) {
567 qCDebug(CUTELYST_CORE) <<
"Loaded translations for" << loc <<
"from"
568 << ts.absoluteFilePath();
571 qCWarning(CUTELYST_CORE) <<
"Can not load translations for" << loc
572 <<
"from" << ts.absoluteFilePath();
575 qCWarning(CUTELYST_CORE)
576 <<
"Can not load translations for invalid locale string" << locString;
581 qCWarning(CUTELYST_CORE)
582 <<
"Can not find translation files for" << filename <<
"in directory" << _dir;
585 qCWarning(CUTELYST_CORE)
586 <<
"Can not load translations from not existing directory:" << _dir;
589 qCWarning(CUTELYST_CORE) <<
"Can not load translations for empty file name.";
601 const QDir dir(directory);
602 if (Q_LIKELY(dir.
exists())) {
604 if (Q_LIKELY(!dirs.empty())) {
606 for (
const QString &subDir : dirs) {
607 const QString relFn = subDir + u
'/' + filename;
613 if (Q_LIKELY(trans->load(
617 qCDebug(CUTELYST_CORE) <<
"Loaded translations for" << l <<
"from"
621 qCWarning(CUTELYST_CORE) <<
"Can not load translations for" << l
625 qCWarning(CUTELYST_CORE)
626 <<
"Can not load translations for invalid locale string:" << subDir;
632 qCWarning(CUTELYST_CORE) <<
"Can not find locale dirs under" << directory;
635 qCWarning(CUTELYST_CORE)
636 <<
"Can not load translations from not existing directory:" << directory;
639 qCWarning(CUTELYST_CORE)
640 <<
"Can not load translations for empty file name or directory name";
646void Cutelyst::ApplicationPrivate::setupHome()
659void ApplicationPrivate::setupChildren(
const QObjectList &children)
662 for (QObject *child : children) {
663 auto controller = qobject_cast<Controller *>(child);
665 q->registerController(controller);
669 auto plugin = qobject_cast<Plugin *>(child);
671 q->registerPlugin(plugin);
675 auto view = qobject_cast<View *>(child);
677 q->registerView(view);
681 auto dispatchType = qobject_cast<DispatchType *>(child);
683 q->registerDispatcher(dispatchType);
689void Cutelyst::ApplicationPrivate::logRequest(
Request *req)
691 QString path = req->path();
693 path = QStringLiteral(
"/");
695 qCDebug(CUTELYST_REQUEST) << req->method() <<
"request for" << path <<
"from"
699 if (!params.isEmpty()) {
700 logRequestParameters(params, QLatin1String(
"Query Parameters are:"));
704 if (!params.isEmpty()) {
705 logRequestParameters(params, QLatin1String(
"Body Parameters are:"));
708 const auto uploads = req->
uploads();
709 if (!uploads.isEmpty()) {
710 logRequestUploads(uploads);
714void Cutelyst::ApplicationPrivate::logRequestParameters(
const ParamsMultiMap ¶ms,
715 const QString &title)
717 QVector<QStringList> table;
718 auto it = params.constBegin();
719 while (it != params.constEnd()) {
720 table.
append({it.key(), it.value()});
723 qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table,
725 QLatin1String(
"Parameter"),
726 QLatin1String(
"Value"),
732void Cutelyst::ApplicationPrivate::logRequestUploads(
const QVector<Cutelyst::Upload *> &uploads)
734 QVector<QStringList> table;
735 for (
Upload *upload : uploads) {
736 table.
append({upload->name(),
738 upload->contentType(),
741 qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table,
743 QLatin1String(
"Parameter"),
744 QLatin1String(
"Filename"),
745 QLatin1String(
"Type"),
746 QLatin1String(
"Size"),
748 QLatin1String(
"File Uploads are:"))
752Component *ApplicationPrivate::createComponentPlugin(
const QString &name,
754 const QString &directory)
758 QDir pluginsDir(directory);
759 QPluginLoader loader;
761 const auto plugins = pluginsDir.entryList(
QDir::Files);
762 for (
const QString &fileName : plugins) {
763 loader.
setFileName(pluginsDir.absoluteFilePath(fileName));
764 const QJsonObject json = loader.
metaData()[QLatin1String(
"MetaData")].toObject();
765 if (json[QLatin1String(
"name")].toString() == name) {
766 QObject *plugin = loader.
instance();
768 factory = qobject_cast<ComponentFactory *>(plugin);
770 qCCritical(CUTELYST_CORE)
771 <<
"Could not create a factory for" << loader.
fileName();
777 qCCritical(CUTELYST_CORE)
784 factories.insert(name, factory);
790#include "moc_application.cpp"
The Cutelyst Application.
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)
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
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.
void addXCutelystVersionHeader()
Component * createComponentPlugin(const QString &name, QObject *parent=nullptr)
QVariant config(const QString &key, const QVariant &defaultValue={}) const
QVector< QLocale > loadTranslationsFromDir(const QString &filename, const QString &directory=QString(), const QString &prefix=QStringLiteral("."), const QString &suffix=QStringLiteral(".qm"))
View * view(const QString &name) const
void loadTranslations(const QString &filename, const QString &directory=QString(), const QString &prefix=QString(), const QString &suffix=QString())
void postForked(Cutelyst::Application *app)
virtual Component * createComponent(QObject *parent=nullptr)=0
The Cutelyst Component base class.
Cutelyst Controller base class
Context * context
The Cutelyst::Context of this request.
Status status
Connection status.
QString addressString() const
QVector< Upload * > uploads() const
ParamsMultiMap bodyParameters() const
ParamsMultiMap queryParameters() const
Cutelyst Upload handles file upload request
Cutelyst View abstract view component
The Cutelyst namespace holds all public Cutelyst API.
QMultiMap< QString, QString > ParamsMultiMap
QString absoluteFilePath(const QString &fileName) const const
QFileInfoList entryInfoList(Filters filters, SortFlags sort) const const
QStringList entryList(Filters filters, SortFlags sort) const const
bool exists() const const
QString absoluteFilePath() const const
QString absolutePath() const const
QString baseName() const const
QString suffix() const const
Language language() const const
const QObjectList & children() const const
QObject * parent() const const
QString errorString() const const
void setFileName(const QString &fileName)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
const QChar at(int position) const const
QString fromLatin1(const char *str, int size)
QString fromLocal8Bit(const char *str, int size)
QString fromUtf8(const char *str, int size)
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString mid(int position, int n) const const
QString number(int n, int base)
QString & replace(int position, int n, QChar after)
QString join(const QString &separator) const const
void sort(Qt::CaseSensitivity cs)
void append(const T &value)
const_reverse_iterator crbegin() const const
const_reverse_iterator crend() const const
bool isEmpty() const const
T value(int i) const const