6#include "application.h"
10#include "controller.h"
11#include "dispatcher.h"
18#include <QCoreApplication>
23using namespace Qt::Literals::StringLiterals;
31 : d_ptr(new ContextPrivate(app, app->engine(), app->dispatcher(), app->plugins()))
33 auto req =
new DummyRequest(
this);
35 req->
body->open(QBuffer::ReadWrite);
40 d_ptr->request->d_ptr->engine = d_ptr->engine;
46 delete d_ptr->request;
47 delete d_ptr->response;
54 return !d->error.isEmpty();
60 if (
error.isEmpty()) {
64 qCCritical(CUTELYST_CORE) <<
error;
119 return d->action->name();
125 return d->action->ns();
143 return d->dispatcher;
149 return d->action->className();
155 return d->action->controller();
161 return d->dispatcher->controller(name);
173 return d->app->view(name);
179 d->view = d->app->view(name);
192 return d->stash.value(key);
198 return d->stash.value(key, defaultValue);
204 return d->stash.take(key);
210 return d->stash.remove(key);
216 d->stash.insert(key, value);
222 d->stash.insert(key, QVariant::fromValue(map));
232 const QStringList &args,
237 QUrl uri = d->request->uri();
240 if (path.isEmpty()) {
242 const QString controllerNS = d->action->controller()->ns();
243 if (!controllerNS.isEmpty()) {
244 _path.prepend(controllerNS);
250 if (!args.isEmpty()) {
251 if (_path.compare(u
"/") == 0) {
252 _path += args.join(u
'/');
254 _path = _path + u
'/' + args.join(u
'/');
258 if (!_path.startsWith(u
'/')) {
261 uri.setPath(_path, QUrl::DecodedMode);
264 if (!queryValues.isEmpty()) {
266 if (!queryValues.isEmpty()) {
267 auto it = queryValues.constEnd();
268 while (it != queryValues.constBegin()) {
270 query.addQueryItem(it.key(), it.value());
280 const QStringList &captures,
281 const QStringList &args,
291 QStringList localArgs = args;
292 QStringList localCaptures = captures;
294 Action *expandedAction = d->dispatcher->expandAction(
this,
action);
296 while (localCaptures.size() < expandedAction->
numberOfCaptures() && !localArgs.isEmpty()) {
297 localCaptures.append(localArgs.takeFirst());
300 QStringList localCapturesAux = localCaptures;
301 localCapturesAux.append(localArgs);
302 localArgs = localCapturesAux;
303 localCaptures = QStringList();
306 const QString path = d->dispatcher->uriForAction(
action, localCaptures);
307 if (path.isEmpty()) {
308 qCWarning(CUTELYST_CORE) <<
"Can not find action for" <<
action << localCaptures;
312 uri =
uriFor(path, localArgs, queryValues);
317 const QStringList &captures,
318 const QStringList &args,
326 qCWarning(CUTELYST_CORE) <<
"Can not find action for" << path;
344 d->dispatcher->forward(
this,
action);
365 if (--d->actionRefCount || !d->stack.isEmpty()) {
369 if (Q_UNLIKELY((d->engineRequest->status & EngineRequest::Finalized) &&
370 !(d->engineRequest->status & EngineRequest::Async))) {
371 qCWarning(CUTELYST_ASYNC) <<
"Trying to async attach to a finalized request! Skipping...";
375 if ((d->engineRequest->status & EngineRequest::Async) &&
376 !(d->engineRequest->status & EngineRequest::Finalized)) {
377 while (!d->pendingAsync.isEmpty()) {
381 if (d->actionRefCount) {
390 Q_EMIT d->app->afterDispatch(
this);
399 return d->dispatcher->forward(
this,
action);
405 return d->dispatcher->forward(
this,
action);
411 return d->dispatcher->getAction(
action,
ns);
417 return d->dispatcher->getActions(
action,
ns);
429 Q_ASSERT_X(code,
"Context::execute",
"trying to execute a null Cutelyst::Component");
431 static int recursion =
432 qEnvironmentVariableIsSet(
"RECURSION") ? qEnvironmentVariableIntValue(
"RECURSION") : 1000;
433 if (d->stack.size() >= recursion) {
434 QString msg = QStringLiteral(
"Deep recursion detected (stack size %1) calling %2, %3")
435 .arg(QString::number(d->stack.size()), code->
reverse(), code->
name());
445 const QString statsInfo = d->statsStartExecute(code);
451 if (d->stats && !statsInfo.isEmpty()) {
452 d->statsFinishExecute(statsInfo);
478 return d->app->config(key, defaultValue);
484 return d->app->config();
488 const char *sourceText,
489 const char *disambiguation,
493 return d->app->translate(d->locale, context, sourceText, disambiguation, n);
500 if (Q_UNLIKELY(d->engineRequest->status & EngineRequest::Finalized)) {
501 qCWarning(CUTELYST_CORE) <<
"Trying to finalize a finalized request! Skipping...";
506 qCDebug(CUTELYST_STATS,
507 "Response Code: %d; Content-Type: %s; Content-Length: %s",
508 d->response->status(),
509 d->response->headers().header(
"Content-Type",
"unknown"_ba).constData(),
510 d->response->headers()
511 .header(
"Content-Length", QByteArray::number(d->response->size()))
514 const std::chrono::duration<double> duration =
515 std::chrono::steady_clock::now() - d->engineRequest->startOfRequest;
518 if (duration.count() == 0.0) {
519 average = QStringLiteral(
"??");
521 average = QString::number(1.0 / duration.count(),
'f');
522 average.truncate(average.size() - 3);
524 qCInfo(CUTELYST_STATS) << qPrintable(QStringLiteral(
"Request took: %1s (%2/s)\n%3")
525 .arg(QString::number(duration.count(),
'f'),
527 QString::fromLatin1(d->stats->report())));
532 d->engineRequest->finalize();
535QString ContextPrivate::statsStartExecute(
Component *code)
539 if (code->
name().startsWith(u
'_')) {
545 if (qobject_cast<Action *>(code)) {
546 actionName.prepend(u
'/');
549 if (stack.size() > 2) {
550 actionName = u
"-> " + actionName;
552 actionName.rightJustified(actionName.size() + stack.size() - 2, QLatin1Char(
' '));
555 stats->profileStart(actionName);
560void ContextPrivate::statsFinishExecute(
const QString &statsInfo)
562 stats->profileEnd(statsInfo);
569 d->stash.insert(unite);
572#include "moc_context.cpp"
573#include "moc_context_p.cpp"
This class represents a Cutelyst Action.
virtual qint8 numberOfCaptures() const
The Cutelyst application.
Headers & defaultHeaders() noexcept
QLocale defaultLocale() const noexcept
The Cutelyst Component base class.
QString reverse() const noexcept
QString name() const noexcept
QStringList errors() const noexcept
bool forward(Component *component)
QVector< Plugin * > plugins() const
Context(Application *app)
void detach(Action *action=nullptr)
QStack< Component * > stack() const noexcept
QLocale locale() const noexcept
void setState(bool state) noexcept
QUrl uriFor(const QString &path={}, const QStringList &args={}, const ParamsMultiMap &queryValues={}) const
Response * res() const noexcept
QString translate(const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
void setStash(const QString &key, const QVariant &value)
bool stashRemove(const QString &key)
QVariant stashTake(const QString &key)
bool setCustomView(QStringView name)
void setLocale(const QLocale &locale)
View * customView() const noexcept
QVector< Action * > getActions(QStringView action, QStringView ns={}) const
bool detached() const noexcept
Action * getAction(QStringView action, QStringView ns={}) const
Dispatcher * dispatcher() const noexcept
Application * app() const noexcept
View * view(QStringView name={}) const
QUrl uriForAction(QStringView path, const QStringList &captures={}, const QStringList &args={}, const ParamsMultiMap &queryValues={}) const
void detachAsync() noexcept
bool execute(Component *code)
void appendError(const QString &error)
Engine * engine() const noexcept
bool error() const noexcept
Response * response() const noexcept
Cutelyst Controller base class.
QIODevice * body() const noexcept
Abstract View component for Cutelyst.
QMultiMap< QString, QString > ParamsMultiMap
The Cutelyst namespace holds all public Cutelyst API.