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 <QJsonDocument> 22 #include <QtCore/QCoreApplication> 23 #include <QtCore/QDataStream> 24 #include <QtCore/QDir> 25 #include <QtCore/QFileInfo> 26 #include <QtCore/QLocale> 27 #include <QtCore/QPluginLoader> 28 #include <QtCore/QStringList> 29 #include <QtCore/QTranslator> 31 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER,
"cutelyst.dispatcher", QtWarningMsg)
32 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_PATH,
"cutelyst.dispatcher.path", QtWarningMsg)
33 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_CHAINED,
"cutelyst.dispatcher.chained", QtWarningMsg)
34 Q_LOGGING_CATEGORY(CUTELYST_CONTROLLER,
"cutelyst.controller", QtWarningMsg)
35 Q_LOGGING_CATEGORY(CUTELYST_CORE,
"cutelyst.core", QtWarningMsg)
36 Q_LOGGING_CATEGORY(CUTELYST_ENGINE,
"cutelyst.engine", QtWarningMsg)
37 Q_LOGGING_CATEGORY(CUTELYST_UPLOAD,
"cutelyst.upload", QtWarningMsg)
38 Q_LOGGING_CATEGORY(CUTELYST_MULTIPART,
"cutelyst.multipart", QtWarningMsg)
39 Q_LOGGING_CATEGORY(CUTELYST_VIEW,
"cutelyst.view", QtWarningMsg)
40 Q_LOGGING_CATEGORY(CUTELYST_REQUEST,
"cutelyst.request", QtWarningMsg)
41 Q_LOGGING_CATEGORY(CUTELYST_RESPONSE,
"cutelyst.response", QtWarningMsg)
42 Q_LOGGING_CATEGORY(CUTELYST_STATS,
"cutelyst.stats", QtWarningMsg)
43 Q_LOGGING_CATEGORY(CUTELYST_COMPONENT,
"cutelyst.component", QtWarningMsg)
49 , d_ptr(new ApplicationPrivate)
55 qRegisterMetaType<ParamsMultiMap>();
69 qCDebug(CUTELYST_CORE) <<
"Default Application::init called on pid:" 76 qCDebug(CUTELYST_CORE) <<
"Default Application::postFork called on pid:" 90 d->headers.setHeader(
"X-Cutelyst"_qba, QByteArrayLiteral(CUTELYST_VERSION));
96 if (d->plugins.contains(
plugin)) {
107 if (d->controllersHash.contains(name)) {
110 d->controllersHash.
insert(name, controller);
111 d->controllers.
append(controller);
118 if (d->views.contains(
view->
name())) {
120 <<
"There is already a view with this name:" <<
view->
name();
140 auto it = d->factories.constFind(name);
141 if (it != d->factories.constEnd()) {
151 qgetenv(
"CUTELYST_PLUGINS_DIR").split(
';');
158 qCDebug(CUTELYST_CORE) <<
"Did not find plugin" << name <<
"on" << dirs <<
"for" <<
parent;
165 return CUTELYST_VERSION;
171 return d->controllers;
177 return d->views.value(name);
183 auto it = d->config.constFind(key);
184 if (it != d->config.constEnd()) {
193 return d->dispatcher;
199 return d->dispatcher->dispatchers();
222 QDir home = config(u
"home"_qs).toString();
241 d->config.insert(key, value);
253 d->useStats = CUTELYST_STATS().isDebugEnabled();
267 const auto plugins = d->plugins;
269 if (
plugin->objectName().isEmpty()) {
277 if (zeroCore && !tablePlugins.
isEmpty()) {
278 qCDebug(CUTELYST_CORE)
285 tableDataHandlers.
append({u
"application/x-www-form-urlencoded"_qs});
286 tableDataHandlers.
append({u
"application/json"_qs});
287 tableDataHandlers.
append({u
"multipart/form-data"_qs});
288 qCDebug(CUTELYST_CORE)
289 << Utils::buildTable(tableDataHandlers,
294 qCDebug(CUTELYST_CORE) <<
"Loaded dispatcher" 296 qCDebug(CUTELYST_CORE)
300 QString home = d->config.value(u
"home"_qs).toString();
303 qCDebug(CUTELYST_CORE) <<
"Couldn't find home";
307 if (homeInfo.
isDir()) {
309 qCDebug(CUTELYST_CORE) <<
"Found home" << home;
313 qCDebug(CUTELYST_CORE) <<
"Home" << home <<
"doesn't exist";
319 QStringList controllerNames = d->controllersHash.keys();
320 controllerNames.
sort();
321 for (
const QString &controller : controllerNames) {
325 const auto views = d->views;
335 if (zeroCore && !table.
isEmpty()) {
336 qCDebug(CUTELYST_CORE)
337 << Utils::buildTable(table,
345 controller->d_ptr->init(
this, d->dispatcher);
348 d->dispatcher->setupActions(d->controllers, d->dispatchers, d->engine->workerCore() == 0);
351 qCInfo(CUTELYST_CORE) << qPrintable(
372 auto priv =
new ContextPrivate(
this,
engine, d->dispatcher, d->plugins);
376 priv->engineRequest = request;
377 priv->response =
new Response(d->headers, request);
378 priv->request =
new Request(request);
379 priv->locale = d->defaultLocale;
382 priv->stats =
new Stats(request);
386 bool skipMethod =
false;
390 static bool log = CUTELYST_REQUEST().isEnabled(QtDebugMsg);
392 d->logRequest(priv->request);
395 d->dispatcher->prepareAction(c);
399 d->dispatcher->dispatch(c);
401 if (request->
status & EngineRequest::Async) {
421 if (!controller->postFork(
this)) {
434 Q_ASSERT_X(translator,
"add translator to application",
"invalid QTranslator object");
435 auto it = d->translators.find(locale);
436 if (it != d->translators.end()) {
437 it.value().prepend(translator);
451 Q_ASSERT_X(!translators.
empty(),
"add translators to application",
"empty translators vector");
452 auto transIt = d->translators.find(locale);
453 if (transIt != d->translators.end()) {
454 for (
auto it = translators.
crbegin(); it != translators.
crend(); ++it) {
455 transIt.value().prepend(*it);
458 d->translators.insert(locale, translators);
462 static void replacePercentN(
QString *result,
int n)
467 while ((percentPos = result->
indexOf(u
'%', percentPos + len)) != -1) {
470 if (result->
at(percentPos + len) == u
'L') {
472 fmt = QStringLiteral(
"%L1");
474 fmt = QStringLiteral(
"%1");
476 if (result->
at(percentPos + len) == u
'n') {
479 result->
replace(percentPos, len, fmt);
488 const char *sourceText,
489 const char *disambiguation,
501 if (translators.
empty()) {
503 replacePercentN(&result, n);
508 result = translator->translate(context, sourceText, disambiguation, n);
518 replacePercentN(&result, n);
537 if (Q_LIKELY(!filename.
isEmpty())) {
538 const QString _dir = directory.
isEmpty() ? QStringLiteral(CUTELYST_I18N_DIR) : directory;
539 const QDir i18nDir(_dir);
540 if (Q_LIKELY(i18nDir.
exists())) {
541 const QString _prefix = prefix.
isEmpty() ? QStringLiteral(
".") : prefix;
542 const QString _suffix = suffix.
isEmpty() ? QStringLiteral(
".qm") : suffix;
546 if (Q_LIKELY(!tsFiles.empty())) {
547 locales.
reserve(tsFiles.size());
549 const QString fn = ts.fileName();
550 const int prefIdx = fn.
indexOf(_prefix);
557 if (Q_LIKELY(trans->load(loc, filename, _prefix, _dir))) {
560 qCDebug(CUTELYST_CORE) <<
"Loaded translations for" << loc <<
"from" 561 << ts.absoluteFilePath();
564 qCWarning(CUTELYST_CORE) <<
"Can not load translations for" << loc
565 <<
"from" << ts.absoluteFilePath();
568 qCWarning(CUTELYST_CORE)
569 <<
"Can not load translations for invalid locale string" << locString;
574 qCWarning(CUTELYST_CORE)
575 <<
"Can not find translation files for" << filename <<
"in directory" << _dir;
578 qCWarning(CUTELYST_CORE)
579 <<
"Can not load translations from not existing directory:" << _dir;
582 qCWarning(CUTELYST_CORE) <<
"Can not load translations for empty file name.";
594 const QDir dir(directory);
595 if (Q_LIKELY(dir.
exists())) {
597 if (Q_LIKELY(!dirs.empty())) {
599 for (
const QString &subDir : dirs) {
600 const QString relFn = subDir + u
'/' + filename;
606 if (Q_LIKELY(trans->load(
610 qCDebug(CUTELYST_CORE) <<
"Loaded translations for" << l <<
"from" 614 qCWarning(CUTELYST_CORE) <<
"Can not load translations for" << l
618 qCWarning(CUTELYST_CORE)
619 <<
"Can not load translations for invalid locale string:" << subDir;
625 qCWarning(CUTELYST_CORE) <<
"Can not find locale dirs under" << directory;
628 qCWarning(CUTELYST_CORE)
629 <<
"Can not load translations from not existing directory:" << directory;
632 qCWarning(CUTELYST_CORE)
633 <<
"Can not load translations for empty file name or directory name";
642 return d->defaultLocale;
648 d->defaultLocale = locale;
651 void Cutelyst::ApplicationPrivate::setupHome()
664 void ApplicationPrivate::setupChildren(
const QObjectList &children)
667 for (
QObject *child : children) {
668 auto controller = qobject_cast<
Controller *>(child);
670 q->registerController(controller);
674 auto plugin = qobject_cast<
Plugin *>(child);
676 q->registerPlugin(plugin);
680 auto view = qobject_cast<
View *>(child);
682 q->registerView(view);
686 auto dispatchType = qobject_cast<
DispatchType *>(child);
688 q->registerDispatcher(dispatchType);
694 void Cutelyst::ApplicationPrivate::logRequest(
Request *req)
698 path = QStringLiteral(
"/");
700 qCDebug(CUTELYST_REQUEST) << req->method() <<
"request for" << path <<
"from" 705 logRequestParameters(params,
QLatin1String(
"Query Parameters are:"));
710 logRequestParameters(params,
QLatin1String(
"Body Parameters are:"));
713 const auto bodyData = req->bodyData();
716 qCDebug(CUTELYST_REQUEST).noquote() <<
"JSON body:\n" 720 const auto uploads = req->
uploads();
721 if (!uploads.isEmpty()) {
722 logRequestUploads(uploads);
726 void Cutelyst::ApplicationPrivate::logRequestParameters(
const ParamsMultiMap ¶ms,
732 table.
append({it.key(), it.value()});
735 qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table,
747 for (
Upload *upload : uploads) {
748 table.
append({upload->name(),
753 qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table,
770 QDir pluginsDir(directory);
773 const auto plugins = pluginsDir.entryList(
QDir::Files);
774 for (
const QString &fileName : plugins) {
775 loader.
setFileName(pluginsDir.absoluteFilePath(fileName));
782 qCCritical(CUTELYST_CORE)
783 <<
"Could not create a factory for" << loader.
fileName();
789 qCCritical(CUTELYST_CORE)
796 factories.
insert(name, factory);
802 #include "moc_application.cpp" void setDefaultLocale(const QLocale &locale)
QVector< DispatchType * > dispatchers() const noexcept
void setConfig(const QString &key, const QVariant &value)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
QString & append(QChar ch)
QVariantMap config() const noexcept
void postForked(Cutelyst::Application *app)
QByteArray toJson(QJsonDocument::JsonFormat format) const const
QJsonDocument toJsonDocument() const const
void setReverse(const QString &reverse)
void reserve(qsizetype size)
QMultiMap::const_iterator constEnd() const const
bool isEmpty() const const
const QObjectList & children() const const
bool registerView(View *view)
QVector< Controller * > controllers() const noexcept
QString join(QStringView separator) const const
QString fromUtf8(QByteArrayView str)
QString reverse() const noexcept
void setFileName(const QString &fileName)
void addXCutelystVersionHeader()
bool setup(Engine *engine)
void addTranslators(const QLocale &locale, const QVector< QTranslator *> &translators)
void preForked(Cutelyst::Application *app)
The Cutelyst Component base class.
void handleRequest(Cutelyst::EngineRequest *request)
T value(qsizetype i) const const
Headers & defaultHeaders() noexcept
Cutelyst Upload handles file upload requests.
QMultiMap::const_iterator constBegin() const const
ParamsMultiMap bodyParameters() const
QString number(int n, int base)
bool exists() const const
Cutelyst Controller base class.
QVector< Upload * > uploads() const
void append(QList::parameter_type value)
QString & insert(qsizetype position, QChar ch)
bool registerController(Controller *controller)
QString addressString() const
QList::const_reverse_iterator crbegin() const const
QString fromLocal8Bit(QByteArrayView str)
QLocale defaultLocale() const noexcept
Component * createComponentPlugin(const QString &name, QObject *parent=nullptr)
QVector< Plugin * > plugins() const noexcept
void beforePrepareAction(Cutelyst::Context *c, bool *skipMethod)
bool isEmpty() const const
QFileInfoList entryInfoList(QDir::Filters filters, QDir::SortFlags sort) const const
QVariantMap config(const QString &entity) const
QString absoluteFilePath() const const
bool isEmpty() const const
void addTranslator(const QLocale &locale, QTranslator *translator)
QString name() const noexcept
QLocale::Language language() const const
QString pathTo(const QString &path) const
QString errorString() const const
The Cutelyst namespace holds all public Cutelyst API.
View * view(QStringView name={}) const
bool registerDispatcher(DispatchType *dispatcher)
void beforeDispatch(Cutelyst::Context *c)
ParamsMultiMap queryParameters() const
Application(QObject *parent=nullptr)
QString & replace(qsizetype position, qsizetype n, QChar after)
QString fromLatin1(QByteArrayView str)
void afterDispatch(Cutelyst::Context *c)
QString mid(qsizetype position, qsizetype n) const const
QString suffix() const const
Dispatcher * dispatcher() const noexcept
bool inited() const noexcept
Abstract class to described a dispatch type.
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(qsizetype position) const const
Abstract View component for Cutelyst.
QString absoluteFilePath(const QString &fileName) const const
qsizetype length() const const
Base class for Cutelyst Plugins.
void sort(Qt::CaseSensitivity cs)
The Cutelyst application.
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)
QList::const_reverse_iterator crend() const const
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()
static const char * cutelystVersion() noexcept
void loadTranslations(const QString &filename, const QString &directory={}, const QString &prefix={}, const QString &suffix={})