5 #include "application_p.h" 9 #include "controller.h" 10 #include "controller_p.h" 11 #include "dispatchtype.h" 14 #include "request_p.h" 16 #include "response_p.h" 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> 30 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER,
"cutelyst.dispatcher", QtWarningMsg)
31 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_PATH,
"cutelyst.dispatcher.path", QtWarningMsg)
32 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_CHAINED,
"cutelyst.dispatcher.chained", QtWarningMsg)
33 Q_LOGGING_CATEGORY(CUTELYST_CONTROLLER,
"cutelyst.controller", QtWarningMsg)
34 Q_LOGGING_CATEGORY(CUTELYST_CORE,
"cutelyst.core", QtWarningMsg)
35 Q_LOGGING_CATEGORY(CUTELYST_ENGINE,
"cutelyst.engine", QtWarningMsg)
36 Q_LOGGING_CATEGORY(CUTELYST_UPLOAD,
"cutelyst.upload", QtWarningMsg)
37 Q_LOGGING_CATEGORY(CUTELYST_MULTIPART,
"cutelyst.multipart", QtWarningMsg)
38 Q_LOGGING_CATEGORY(CUTELYST_VIEW,
"cutelyst.view", QtWarningMsg)
39 Q_LOGGING_CATEGORY(CUTELYST_REQUEST,
"cutelyst.request", QtWarningMsg)
40 Q_LOGGING_CATEGORY(CUTELYST_RESPONSE,
"cutelyst.response", QtWarningMsg)
41 Q_LOGGING_CATEGORY(CUTELYST_STATS,
"cutelyst.stats", QtWarningMsg)
42 Q_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");
64 Application::~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())) {
122 <<
"There is already a view with this name:" <<
view->
name();
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();
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;
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(
380 auto priv =
new ContextPrivate(
this,
engine, d->dispatcher, d->plugins);
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);
469 static 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";
646 void Cutelyst::ApplicationPrivate::setupHome()
659 void 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);
689 void Cutelyst::ApplicationPrivate::logRequest(
Request *req)
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);
714 void Cutelyst::ApplicationPrivate::logRequestParameters(
const ParamsMultiMap ¶ms,
720 table.
append({it.key(), it.value()});
723 qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table,
735 for (
Upload *upload : uploads) {
736 table.
append({upload->name(),
738 upload->contentType(),
741 qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table,
758 QDir pluginsDir(directory);
761 const auto plugins = pluginsDir.entryList(
QDir::Files);
762 for (
const QString &fileName : plugins) {
763 loader.
setFileName(pluginsDir.absoluteFilePath(fileName));
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" QVector< DispatchType * > dispatchers() const noexcept
void setConfig(const QString &key, const QVariant &value)
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QString & append(QChar ch)
QVariantMap config() const noexcept
QVector::const_reverse_iterator crbegin() const const
void postForked(Cutelyst::Application *app)
void append(const T &value)
void setReverse(const QString &reverse)
QMap::const_iterator constBegin() const const
const QObjectList & children() const const
void insert(int i, T &&value)
View * view(const QString &name) const
bool registerView(View *view)
void loadTranslations(const QString &filename, const QString &directory=QString(), const QString &prefix=QString(), const QString &suffix=QString())
QVector< Controller * > controllers() const noexcept
QString join(const QString &separator) const const
T plugin()
Returns the registered plugin that casts to the template type T.
void setFileName(const QString &fileName)
void addXCutelystVersionHeader()
bool setup(Engine *engine)
Called by the Engine to setup the internal data.
void addTranslators(const QLocale &locale, const QVector< QTranslator *> &translators)
void preForked(Cutelyst::Application *app)
The Cutelyst Component base class.
void handleRequest(Cutelyst::EngineRequest *request)
Called by the Engine to handle a new Request object.
T value(int i) const const
Headers & defaultHeaders() noexcept
Cutelyst Upload handles file upload request
ParamsMultiMap bodyParameters() const
QString number(int n, int base)
QString fromLocal8Bit(const char *str, int size)
bool exists() const const
Cutelyst Controller base class
QVector< Upload * > uploads() const
QString fromUtf8(const char *str, int size)
QString & insert(int position, QChar ch)
bool registerController(Controller *controller)
QString addressString() const
Component * createComponentPlugin(const QString &name, QObject *parent=nullptr)
QVector< Plugin * > plugins() const noexcept
void beforePrepareAction(Cutelyst::Context *c, bool *skipMethod)
QFileInfoList entryInfoList(QDir::Filters filters, QDir::SortFlags sort) const const
Context * context
The Cutelyst::Context of this request.
QVariantMap config(const QString &entity) const
user configuration for the application
QString absoluteFilePath() const const
bool isEmpty() const const
QMap::const_iterator constEnd() const const
void addTranslator(const QLocale &locale, QTranslator *translator)
QLocale::Language language() const const
QString pathTo(const QString &path) const
QString errorString() const const
The Cutelyst namespace holds all public Cutelyst API.
bool registerDispatcher(DispatchType *dispatcher)
void beforeDispatch(Cutelyst::Context *c)
ParamsMultiMap queryParameters() const
Application(QObject *parent=nullptr)
QString & replace(int position, int n, QChar after)
bool enginePostFork()
Called by the Engine once post fork happened.
void afterDispatch(Cutelyst::Context *c)
QString mid(int position, int n) const const
QString suffix() const const
Dispatcher * dispatcher() const noexcept
bool isEmpty() const const
bool inited() const noexcept
QStringList entryList(QDir::Filters filters, QDir::SortFlags sort) const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
const QChar at(int position) const const
Cutelyst View abstract view component
Status status
Connection status.
QString absoluteFilePath(const QString &fileName) const const
QString fromLatin1(const char *str, int size)
void sort(Qt::CaseSensitivity cs)
The Cutelyst Application.
bool isEmpty() const const
Engine * engine() const noexcept
bool registerPlugin(Plugin *plugin)
QString absolutePath() const const
virtual Component * createComponent(QObject *parent=nullptr)=0
QObject * parent() const const
QVector< QLocale > loadTranslationsFromDir(const QString &filename, const QString &directory=QString(), const QString &prefix=QStringLiteral("."), const QString &suffix=QStringLiteral(".qm"))
QVector< QLocale > loadTranslationsFromDirs(const QString &directory, const QString &filename)
int workerCore() const
Each worker process migth have a number of worker cores (threads), a single process with two worker ...
QString translate(const QLocale &locale, const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
QString baseName() const const
QString applicationName()
QVector::const_reverse_iterator crend() const const
static const char * cutelystVersion() noexcept