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"
700 logRequestParameters(params, QLatin1String(
"Query Parameters are:"));
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;
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)
778 <<
"Could not load plugin" << loader.fileName() << loader.errorString();
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(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
QString suffix() const const
void append(QList< T > &&value)
QList< T >::const_reverse_iterator crbegin() const const
QList< T >::const_reverse_iterator crend() const const
bool isEmpty() const const
void reserve(qsizetype size)
T value(qsizetype i) const const
QLocale::Language language() const const
QMultiMap< Key, T >::const_iterator constBegin() const const
QMultiMap< Key, T >::const_iterator constEnd() const const
bool isEmpty() const const
const QObjectList & children() const const
QObject * parent() const const
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
bool isEmpty() const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) &&
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)