18 #include "application_p.h"
22 #include "context_p.h"
25 #include "request_p.h"
26 #include "controller.h"
27 #include "controller_p.h"
29 #include "response_p.h"
30 #include "dispatchtype.h"
35 #include <QtCore/QDir>
36 #include <QtCore/QStringList>
37 #include <QtCore/QDataStream>
38 #include <QtCore/QCoreApplication>
39 #include <QtCore/QPluginLoader>
40 #include <QtCore/QTranslator>
41 #include <QtCore/QFileInfo>
42 #include <QtCore/QLocale>
44 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER,
"cutelyst.dispatcher", QtWarningMsg)
45 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_PATH,
"cutelyst.dispatcher.path", QtWarningMsg)
46 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_CHAINED,
"cutelyst.dispatcher.chained", QtWarningMsg)
47 Q_LOGGING_CATEGORY(CUTELYST_CONTROLLER,
"cutelyst.controller", QtWarningMsg)
48 Q_LOGGING_CATEGORY(CUTELYST_CORE,
"cutelyst.core", QtWarningMsg)
49 Q_LOGGING_CATEGORY(CUTELYST_ENGINE,
"cutelyst.engine", QtWarningMsg)
50 Q_LOGGING_CATEGORY(CUTELYST_UPLOAD,
"cutelyst.upload", QtWarningMsg)
51 Q_LOGGING_CATEGORY(CUTELYST_MULTIPART,
"cutelyst.multipart", QtWarningMsg)
52 Q_LOGGING_CATEGORY(CUTELYST_VIEW,
"cutelyst.view", QtWarningMsg)
53 Q_LOGGING_CATEGORY(CUTELYST_REQUEST,
"cutelyst.request", QtWarningMsg)
54 Q_LOGGING_CATEGORY(CUTELYST_RESPONSE,
"cutelyst.response", QtWarningMsg)
55 Q_LOGGING_CATEGORY(CUTELYST_STATS,
"cutelyst.stats", QtWarningMsg)
56 Q_LOGGING_CATEGORY(CUTELYST_COMPONENT,
"cutelyst.component", QtWarningMsg)
62 d_ptr(new ApplicationPrivate)
68 qRegisterMetaType<ParamsMultiMap>();
69 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
70 qRegisterMetaTypeStreamOperators<ParamsMultiMap>(
"ParamsMultiMap");
78 Application::~Application()
104 d->headers.setHeader(QStringLiteral(
"X_CUTELYST"), QStringLiteral(VERSION));
110 if (d->plugins.contains(
plugin)) {
113 d->plugins.append(
plugin);
121 if (d->controllersHash.contains(name)) {
124 d->controllersHash.insert(name, controller);
125 d->controllers.append(controller);
132 if (d->views.contains(
view->
name())) {
134 <<
"There is already a view with this name:" <<
view->
name();
154 auto it = d->factories.constFind(name);
155 if (it != d->factories.constEnd()) {
171 qCDebug(CUTELYST_CORE) <<
"Did not find plugin" << name <<
"on" << dirs <<
"for" <<
parent;
184 return d->controllers;
190 return d->views.value(name);
196 auto it = d->config.constFind(key);
197 if (it != d->config.constEnd()) {
206 return d->dispatcher;
212 return d->dispatcher->dispatchers();
229 QDir home =
config(QStringLiteral(
"home")).toString();
235 QDir home = config(QStringLiteral(
"home")).toString();
254 d->config.insert(key, value);
266 d->useStats = CUTELYST_STATS().isDebugEnabled();
280 const auto plugins = d->plugins;
282 if (
plugin->objectName().isEmpty()) {
290 if (zeroCore && !tablePlugins.
isEmpty()) {
291 qCDebug(CUTELYST_CORE) << Utils::buildTable(tablePlugins,
QStringList(),
300 qCDebug(CUTELYST_CORE) << Utils::buildTable(tableDataHandlers,
QStringList(),
303 qCDebug(CUTELYST_CORE) <<
"Loaded dispatcher" <<
QString::fromLatin1(d->dispatcher->metaObject()->className());
304 qCDebug(CUTELYST_CORE) <<
"Using engine" <<
QString::fromLatin1(d->engine->metaObject()->className());
310 qCDebug(CUTELYST_CORE) <<
"Couldn't find home";
314 if (homeInfo.
isDir()) {
316 qCDebug(CUTELYST_CORE) <<
"Found home" << home;
320 qCDebug(CUTELYST_CORE) <<
"Home" << home <<
"doesn't exist";
326 QStringList controllerNames = d->controllersHash.keys();
327 controllerNames.
sort();
328 for (
const QString &controller : controllerNames) {
332 const auto views = d->views;
341 if (zeroCore && !table.
isEmpty()) {
342 qCDebug(CUTELYST_CORE) << Utils::buildTable(table, {
350 controller->d_ptr->init(
this, d->dispatcher);
353 d->dispatcher->setupActions(d->controllers, d->dispatchers, d->engine->workerCore() == 0);
356 qCInfo(CUTELYST_CORE) << qPrintable(
QString::fromLatin1(
"%1 powered by Cutelyst %2, Qt %3.")
376 auto priv =
new ContextPrivate(
this,
engine, d->dispatcher, d->plugins);
380 priv->engineRequest = request;
381 priv->response =
new Response(d->headers, request);
382 priv->request =
new Request(request);
385 priv->stats =
new Stats(request);
389 bool skipMethod =
false;
393 static bool log = CUTELYST_REQUEST().isEnabled(QtDebugMsg);
395 d->logRequest(priv->request);
398 d->dispatcher->prepareAction(c);
402 d->dispatcher->dispatch(c);
404 if (request->
status & EngineRequest::Async) {
424 if (!controller->postFork(
this)) {
437 Q_ASSERT_X(translator,
"add translator to application",
"invalid QTranslator object");
438 auto it = d->translators.find(locale);
439 if (it != d->translators.end()) {
440 it.value().prepend(translator);
454 Q_ASSERT_X(!translators.
empty(),
"add translators to application",
"empty translators vector");
455 auto transIt = d->translators.find(locale);
456 if (transIt != d->translators.end()) {
457 for (
auto it = translators.
crbegin(); it != translators.
crend(); ++it) {
458 transIt.value().prepend(*it);
461 d->translators.insert(locale, translators);
465 static void replacePercentN(
QString *result,
int n)
475 fmt = QStringLiteral(
"%L1");
477 fmt = QStringLiteral(
"%1");
482 result->
replace(percentPos, len, fmt);
500 if (translators.
empty()) {
502 replacePercentN(&result, n);
507 result = translator->translate(context, sourceText, disambiguation, n);
517 replacePercentN(&result, n);
530 if (Q_LIKELY(!filename.
isEmpty())) {
531 const QString _dir = directory.
isEmpty() ? QStringLiteral(I18NDIR) : directory;
532 const QDir i18nDir(_dir);
533 if (Q_LIKELY(i18nDir.
exists())) {
534 const QString _prefix = prefix.
isEmpty() ? QStringLiteral(
".") : prefix;
535 const QString _suffix = suffix.
isEmpty() ? QStringLiteral(
".qm") : suffix;
539 if (Q_LIKELY(!tsFiles.empty())) {
540 locales.
reserve(tsFiles.size());
542 const QString fn = ts.fileName();
543 const int prefIdx = fn.
indexOf(_prefix);
548 if (Q_LIKELY(trans->load(loc, filename, _prefix, _dir))) {
551 qCDebug(CUTELYST_CORE) <<
"Loaded translations for" << loc <<
"from" << ts.absoluteFilePath();
554 qCWarning(CUTELYST_CORE) <<
"Can not load translations for" << loc <<
"from" << ts.absoluteFilePath();
557 qCWarning(CUTELYST_CORE) <<
"Can not load translations for invalid locale string" << locString;
562 qCWarning(CUTELYST_CORE) <<
"Can not find translation files for" << filename <<
"in directory" << _dir;
565 qCWarning(CUTELYST_CORE) <<
"Can not load translations from not existing directory:" << _dir;
568 qCWarning(CUTELYST_CORE) <<
"Can not load translations for empty file name.";
579 const QDir dir(directory);
580 if (Q_LIKELY(dir.
exists())) {
582 if (Q_LIKELY(!dirs.empty())) {
584 for (
const QString &subDir : dirs) {
594 qCDebug(CUTELYST_CORE) <<
"Loaded translations for" << l <<
"from" << fi.
absoluteFilePath();
597 qCWarning(CUTELYST_CORE) <<
"Can not load translations for" << l <<
"from" << fi.
absoluteFilePath();
600 qCWarning(CUTELYST_CORE) <<
"Can not load translations for invalid locale string:" << subDir;
606 qCWarning(CUTELYST_CORE) <<
"Can not find locale dirs under" << directory;
609 qCWarning(CUTELYST_CORE) <<
"Can not load translations from not existing directory:" << directory;
612 qCWarning(CUTELYST_CORE) <<
"Can not load translations for empty file name or directory name";
618 void Cutelyst::ApplicationPrivate::setupHome()
631 void ApplicationPrivate::setupChildren(
const QObjectList &children)
634 for (
QObject *child : children) {
635 auto controller = qobject_cast<Controller *>(child);
637 q->registerController(controller);
641 auto plugin = qobject_cast<Plugin *>(child);
643 q->registerPlugin(plugin);
647 auto view = qobject_cast<View *>(child);
649 q->registerView(view);
653 auto dispatchType = qobject_cast<DispatchType *>(child);
655 q->registerDispatcher(dispatchType);
661 void Cutelyst::ApplicationPrivate::logRequest(
Request *req)
665 path = QStringLiteral(
"/");
667 qCDebug(CUTELYST_REQUEST) << req->method() <<
"request for" << path <<
"from" << req->
addressString();
671 logRequestParameters(params,
QLatin1String(
"Query Parameters are:"));
676 logRequestParameters(params,
QLatin1String(
"Body Parameters are:"));
679 const auto uploads = req->
uploads();
680 if (!uploads.isEmpty()) {
681 logRequestUploads(uploads);
685 void Cutelyst::ApplicationPrivate::logRequestParameters(
const ParamsMultiMap ¶ms,
const QString &title)
690 table.
append({ it.key(), it.value() });
693 qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table, {
703 for (
Upload *upload : uploads) {
704 table.
append({ upload->name(),
706 upload->contentType(),
710 qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table, {
723 QDir pluginsDir(directory);
726 const auto plugins = pluginsDir.entryList(
QDir::Files);
727 for (
const QString &fileName : plugins) {
728 loader.
setFileName(pluginsDir.absoluteFilePath(fileName));
733 factory = qobject_cast<ComponentFactory *>(plugin);
735 qCCritical(CUTELYST_CORE) <<
"Could not create a factory for" << loader.
fileName();
741 qCCritical(CUTELYST_CORE) <<
"Could not load plugin" << loader.
fileName() << loader.
errorString();
747 factories.
insert(name, factory);
753 #include "moc_application.cpp"
The Cutelyst Application.
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)
Dispatcher * dispatcher() const
Application(QObject *parent=nullptr)
void setConfig(const QString &key, const QVariant &value)
static const char * cutelystVersion()
bool registerPlugin(Plugin *plugin)
bool registerController(Controller *controller)
QVector< Plugin * > plugins() const
bool registerDispatcher(DispatchType *dispatcher)
QString pathTo(const QString &path) const
QString translate(const QLocale &locale, const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
QVariantMap config() const
QVector< DispatchType * > dispatchers() const
void beforeDispatch(Cutelyst::Context *c)
void preForked(Cutelyst::Application *app)
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< QLocale > loadTranslationsFromDirs(const QString &directory, const QString &filename)
T plugin()
Returns the registered plugin that casts to the template type T.
Headers & defaultHeaders()
View * view(const QString &name=QString()) const
void addXCutelystVersionHeader()
Component * createComponentPlugin(const QString &name, QObject *parent=nullptr)
QVector< Controller * > controllers() const
QVector< QLocale > loadTranslationsFromDir(const QString &filename, const QString &directory=QString(), const QString &prefix=QStringLiteral("."), const QString &suffix=QStringLiteral(".qm"))
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.
void setReverse(const QString &reverse)
Cutelyst Controller base class
Context * context
The Cutelyst::Context of this request.
Status status
Connection status.
int workerCore() const
Each worker process migth have a number of worker cores (threads), a single process with two worker t...
QVariantMap config(const QString &entity) const
user configuration for the application
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.
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
QLocale::Language language() const const
QMap::const_iterator constBegin() const const
QMap::const_iterator constEnd() const const
bool isEmpty() 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
QString & insert(int position, QChar ch)
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)
QVector::const_reverse_iterator crbegin() const const
QVector::const_reverse_iterator crend() const const
void insert(int i, T &&value)
bool isEmpty() const const
T value(int i) const const