cutelyst  3.9.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
session.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2013-2022 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "session_p.h"
6 #include "sessionstorefile.h"
7 
8 #include <Cutelyst/Application>
9 #include <Cutelyst/Context>
10 #include <Cutelyst/Engine>
11 #include <Cutelyst/Response>
12 
13 #include <QCoreApplication>
14 #include <QHostAddress>
15 #include <QLoggingCategory>
16 #include <QUuid>
17 
18 using namespace Cutelyst;
19 
20 Q_LOGGING_CATEGORY(C_SESSION, "cutelyst.plugin.session", QtWarningMsg)
21 
22 #define SESSION_VALUES QStringLiteral("_c_session_values")
23 #define SESSION_EXPIRES QStringLiteral("_c_session_expires")
24 #define SESSION_TRIED_LOADING_EXPIRES QStringLiteral("_c_session_tried_loading_expires")
25 #define SESSION_EXTENDED_EXPIRES QStringLiteral("_c_session_extended_expires")
26 #define SESSION_UPDATED QStringLiteral("_c_session_updated")
27 #define SESSION_ID QStringLiteral("_c_session_id")
28 #define SESSION_TRIED_LOADING_ID QStringLiteral("_c_session_tried_loading_id")
29 #define SESSION_DELETED_ID QStringLiteral("_c_session_deleted_id")
30 #define SESSION_DELETE_REASON QStringLiteral("_c_session_delete_reason")
31 
32 static thread_local Session *m_instance = nullptr;
33 
35  : Plugin(parent)
36  , d_ptr(new SessionPrivate(this))
37 {
38 }
39 
40 Cutelyst::Session::~Session()
41 {
42  delete d_ptr;
43 }
44 
46 {
47  Q_D(Session);
48  d->sessionName = QCoreApplication::applicationName() + QLatin1String("_session");
49 
50  const QVariantMap config = app->engine()->config(QLatin1String("Cutelyst_Session_Plugin"));
51  d->sessionExpires = config.value(QLatin1String("expires"), 7200).toLongLong();
52  d->expiryThreshold = config.value(QLatin1String("expiry_threshold"), 0).toLongLong();
53  d->verifyAddress = config.value(QLatin1String("verify_address"), false).toBool();
54  d->verifyUserAgent = config.value(QLatin1String("verify_user_agent"), false).toBool();
55  d->cookieHttpOnly = config.value(QLatin1String("cookie_http_only"), true).toBool();
56  d->cookieSecure = config.value(QLatin1String("cookie_secure"), false).toBool();
57  const QString _sameSite =
58  config.value(QLatin1String("cookie_same_site"), QStringLiteral("strict")).toString();
59 #if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
60  if (_sameSite.compare(u"default", Qt::CaseInsensitive) == 0) {
61  d->cookieSameSite = QNetworkCookie::SameSite::Default;
62  } else if (_sameSite.compare(u"none", Qt::CaseInsensitive) == 0) {
63  d->cookieSameSite = QNetworkCookie::SameSite::None;
64  } else if (_sameSite.compare(u"lax", Qt::CaseInsensitive) == 0) {
65  d->cookieSameSite = QNetworkCookie::SameSite::Lax;
66  } else {
67  d->cookieSameSite = QNetworkCookie::SameSite::Strict;
68  }
69 #else
70  if (_sameSite.compare(u"default", Qt::CaseInsensitive) == 0) {
71  d->cookieSameSite = Cookie::SameSite::Default;
72  } else if (_sameSite.compare(u"none", Qt::CaseInsensitive) == 0) {
73  d->cookieSameSite = Cookie::SameSite::None;
74  } else if (_sameSite.compare(u"lax", Qt::CaseInsensitive) == 0) {
75  d->cookieSameSite = Cookie::SameSite::Lax;
76  } else {
77  d->cookieSameSite = Cookie::SameSite::Strict;
78  }
79 #endif
80 
81  connect(app, &Application::afterDispatch, this, &SessionPrivate::_q_saveSession);
82  connect(app, &Application::postForked, this, [=] { m_instance = this; });
83 
84  if (!d->store) {
85  d->store = new SessionStoreFile(this);
86  }
87 
88  return true;
89 }
90 
92 {
93  Q_D(Session);
94  Q_ASSERT_X(d->store, "Cutelyst::Session::setStorage", "Session Storage is alread defined");
95  store->setParent(this);
96  d->store = store;
97 }
98 
100 {
101  Q_D(const Session);
102  return d->store;
103 }
104 
106 {
107  QString ret;
108  const QVariant sid = c->stash(SESSION_ID);
109  if (sid.isNull()) {
110  if (Q_UNLIKELY(!m_instance)) {
111  qCCritical(C_SESSION) << "Session plugin not registered";
112  return ret;
113  }
114 
115  ret = SessionPrivate::loadSessionId(c, m_instance->d_ptr->sessionName);
116  } else {
117  ret = sid.toString();
118  }
119 
120  return ret;
121 }
122 
124 {
125  QVariant expires = c->stash(SESSION_EXTENDED_EXPIRES);
126  if (!expires.isNull()) {
127  return expires.toULongLong();
128  }
129 
130  if (Q_UNLIKELY(!m_instance)) {
131  qCCritical(C_SESSION) << "Session plugin not registered";
132  return 0;
133  }
134 
135  expires = SessionPrivate::loadSessionExpires(m_instance, c, id(c));
136  if (!expires.isNull()) {
137  return quint64(SessionPrivate::extendSessionExpires(m_instance, c, expires.toLongLong()));
138  }
139 
140  return 0;
141 }
142 
143 void Session::changeExpires(Context *c, quint64 expires)
144 {
145  const QString sid = Session::id(c);
146  const qint64 timeExp = QDateTime::currentMSecsSinceEpoch() / 1000 + qint64(expires);
147 
148  if (Q_UNLIKELY(!m_instance)) {
149  qCCritical(C_SESSION) << "Session plugin not registered";
150  return;
151  }
152 
153  m_instance->d_ptr->store->storeSessionData(c, sid, QStringLiteral("expires"), timeExp);
154 }
155 
156 void Session::deleteSession(Context *c, const QString &reason)
157 {
158  if (Q_UNLIKELY(!m_instance)) {
159  qCCritical(C_SESSION) << "Session plugin not registered";
160  return;
161  }
162  SessionPrivate::deleteSession(m_instance, c, reason);
163 }
164 
166 {
167  return c->stash(SESSION_DELETE_REASON).toString();
168 }
169 
170 QVariant Session::value(Cutelyst::Context *c, const QString &key, const QVariant &defaultValue)
171 {
172  QVariant ret = defaultValue;
173  QVariant session = c->stash(SESSION_VALUES);
174  if (session.isNull()) {
175  session = SessionPrivate::loadSession(c);
176  }
177 
178  if (!session.isNull()) {
179  ret = session.toHash().value(key, defaultValue);
180  }
181 
182  return ret;
183 }
184 
185 void Session::setValue(Cutelyst::Context *c, const QString &key, const QVariant &value)
186 {
187  QVariant session = c->stash(SESSION_VALUES);
188  if (session.isNull()) {
189  session = SessionPrivate::loadSession(c);
190  if (session.isNull()) {
191  if (Q_UNLIKELY(!m_instance)) {
192  qCCritical(C_SESSION) << "Session plugin not registered";
193  return;
194  }
195 
196  SessionPrivate::createSessionIdIfNeeded(
197  m_instance, c, m_instance->d_ptr->sessionExpires);
198  session = SessionPrivate::initializeSessionData(m_instance, c);
199  }
200  }
201 
202  QVariantHash data = session.toHash();
203  data.insert(key, value);
204 
205  c->setStash(SESSION_VALUES, data);
206  c->setStash(SESSION_UPDATED, true);
207 }
208 
210 {
211  QVariant session = c->stash(SESSION_VALUES);
212  if (session.isNull()) {
213  session = SessionPrivate::loadSession(c);
214  if (session.isNull()) {
215  if (Q_UNLIKELY(!m_instance)) {
216  qCCritical(C_SESSION) << "Session plugin not registered";
217  return;
218  }
219 
220  SessionPrivate::createSessionIdIfNeeded(
221  m_instance, c, m_instance->d_ptr->sessionExpires);
222  session = SessionPrivate::initializeSessionData(m_instance, c);
223  }
224  }
225 
226  QVariantHash data = session.toHash();
227  data.remove(key);
228 
229  c->setStash(SESSION_VALUES, data);
230  c->setStash(SESSION_UPDATED, true);
231 }
232 
234 {
235  QVariant session = c->stash(SESSION_VALUES);
236  if (session.isNull()) {
237  session = SessionPrivate::loadSession(c);
238  if (session.isNull()) {
239  if (Q_UNLIKELY(!m_instance)) {
240  qCCritical(C_SESSION) << "Session plugin not registered";
241  return;
242  }
243 
244  SessionPrivate::createSessionIdIfNeeded(
245  m_instance, c, m_instance->d_ptr->sessionExpires);
246  session = SessionPrivate::initializeSessionData(m_instance, c);
247  }
248  }
249 
250  QVariantHash data = session.toHash();
251  for (const QString &key : keys) {
252  data.remove(key);
253  }
254 
255  c->setStash(SESSION_VALUES, data);
256  c->setStash(SESSION_UPDATED, true);
257 }
258 
260 {
261  return !SessionPrivate::loadSession(c).isNull();
262 }
263 
264 QString SessionPrivate::generateSessionId()
265 {
266  return QString::fromLatin1(QUuid::createUuid().toRfc4122().toHex());
267 }
268 
269 QString SessionPrivate::loadSessionId(Context *c, const QString &sessionName)
270 {
271  QString ret;
272  if (!c->stash(SESSION_TRIED_LOADING_ID).isNull()) {
273  return ret;
274  }
275  c->setStash(SESSION_TRIED_LOADING_ID, true);
276 
277  const QString sid = getSessionId(c, sessionName);
278  if (!sid.isEmpty()) {
279  if (!validateSessionId(sid)) {
280  qCCritical(C_SESSION) << "Tried to set invalid session ID" << sid;
281  return ret;
282  }
283  ret = sid;
284  c->setStash(SESSION_ID, sid);
285  }
286 
287  return ret;
288 }
289 
290 QString SessionPrivate::getSessionId(Context *c, const QString &sessionName)
291 {
292  QString ret;
293  bool deleted = !c->stash(SESSION_DELETED_ID).isNull();
294 
295  if (!deleted) {
296  const QVariant property = c->stash(SESSION_ID);
297  if (!property.isNull()) {
298  ret = property.toString();
299  return ret;
300  }
301 
302  const QString cookie = c->request()->cookie(sessionName);
303  if (!cookie.isEmpty()) {
304  qCDebug(C_SESSION) << "Found sessionid" << cookie << "in cookie";
305  ret = cookie;
306  }
307  }
308 
309  return ret;
310 }
311 
312 QString SessionPrivate::createSessionIdIfNeeded(Session *session, Context *c, qint64 expires)
313 {
314  QString ret;
315  const QVariant sid = c->stash(SESSION_ID);
316  if (!sid.isNull()) {
317  ret = sid.toString();
318  } else {
319  ret = createSessionId(session, c, expires);
320  }
321  return ret;
322 }
323 
324 QString SessionPrivate::createSessionId(Session *session, Context *c, qint64 expires)
325 {
326  Q_UNUSED(expires)
327  const QString sid = generateSessionId();
328 
329  qCDebug(C_SESSION) << "Created session" << sid;
330 
331  c->setStash(SESSION_ID, sid);
332  resetSessionExpires(session, c, sid);
333  setSessionId(session, c, sid);
334 
335  return sid;
336 }
337 
338 void SessionPrivate::_q_saveSession(Context *c)
339 {
340  // fix cookie before we send headers
341  saveSessionExpires(c);
342 
343  // Force extension of session_expires before finalizing headers, so a pos
344  // up to date. First call to session_expires will extend the expiry, methods
345  // just return the previously extended value.
346  Session::expires(c);
347 
348  // Persist data
349  if (Q_UNLIKELY(!m_instance)) {
350  qCCritical(C_SESSION) << "Session plugin not registered";
351  return;
352  }
353  saveSessionExpires(c);
354 
355  if (!c->stash(SESSION_UPDATED).toBool()) {
356  return;
357  }
358  SessionStore *store = m_instance->d_ptr->store;
359  QVariantHash sessionData = c->stash(SESSION_VALUES).toHash();
360  sessionData.insert(QStringLiteral("__updated"), QDateTime::currentMSecsSinceEpoch() / 1000);
361 
362  const QString sid = c->stash(SESSION_ID).toString();
363  store->storeSessionData(c, sid, QStringLiteral("session"), sessionData);
364 }
365 
366 void SessionPrivate::deleteSession(Session *session, Context *c, const QString &reason)
367 {
368  qCDebug(C_SESSION) << "Deleting session" << reason;
369 
370  const QVariant sidVar = c->stash(SESSION_ID).toString();
371  if (!sidVar.isNull()) {
372  const QString sid = sidVar.toString();
373  session->d_ptr->store->deleteSessionData(c, sid, QStringLiteral("session"));
374  session->d_ptr->store->deleteSessionData(c, sid, QStringLiteral("expires"));
375  session->d_ptr->store->deleteSessionData(c, sid, QStringLiteral("flash"));
376 
377  deleteSessionId(session, c, sid);
378  }
379 
380  // Reset the values in Context object
381  c->setStash(SESSION_VALUES, QVariant());
382  c->setStash(SESSION_ID, QVariant());
383  c->setStash(SESSION_EXPIRES, QVariant());
384 
385  c->setStash(SESSION_DELETE_REASON, reason);
386 }
387 
388 void SessionPrivate::deleteSessionId(Session *session, Context *c, const QString &sid)
389 {
390  c->setStash(SESSION_DELETED_ID, true); // to prevent get_session_id from returning it
391 
392 #if (QT_VERSION >= QT_VERSION_CHECK(6, 1, 0))
393  updateSessionCookie(c, makeSessionCookie(session, c, sid, QDateTime::currentDateTimeUtc()));
394 #else
395  updateSessionCuteCookie(
396  c, makeSessionCuteCookie(session, c, sid, QDateTime::currentDateTimeUtc()));
397 #endif
398 }
399 
400 QVariant SessionPrivate::loadSession(Context *c)
401 {
402  QVariant ret;
403  const QVariant property = c->stash(SESSION_VALUES);
404  if (!property.isNull()) {
405  ret = property.toHash();
406  return ret;
407  }
408 
409  if (Q_UNLIKELY(!m_instance)) {
410  qCCritical(C_SESSION) << "Session plugin not registered";
411  return ret;
412  }
413 
414  const QString sid = Session::id(c);
415  if (!loadSessionExpires(m_instance, c, sid).isNull()) {
416  if (SessionPrivate::validateSessionId(sid)) {
417 
418  const QVariantHash sessionData =
419  m_instance->d_ptr->store->getSessionData(c, sid, QStringLiteral("session"))
420  .toHash();
421  c->setStash(SESSION_VALUES, sessionData);
422 
423  if (m_instance->d_ptr->verifyAddress &&
424  sessionData.contains(QStringLiteral("__address")) &&
425  sessionData.value(QStringLiteral("__address")).toString() !=
426  c->request()->address().toString()) {
427  qCWarning(C_SESSION) << "Deleting session" << sid << "due to address mismatch:"
428  << sessionData.value(QStringLiteral("__address")).toString()
429  << "!=" << c->request()->address().toString();
430  deleteSession(m_instance, c, QStringLiteral("address mismatch"));
431  return ret;
432  }
433 
434  if (m_instance->d_ptr->verifyUserAgent &&
435  sessionData.contains(QStringLiteral("__user_agent")) &&
436  sessionData.value(QStringLiteral("__user_agent")).toString() !=
437  c->request()->userAgent()) {
438  qCWarning(C_SESSION) << "Deleting session" << sid << "due to user agent mismatch:"
439  << sessionData.value(QStringLiteral("__user_agent")).toString()
440  << "!=" << c->request()->userAgent();
441  deleteSession(m_instance, c, QStringLiteral("user agent mismatch"));
442  return ret;
443  }
444 
445  qCDebug(C_SESSION) << "Restored session" << sid;
446 
447  ret = sessionData;
448  }
449  }
450 
451  return ret;
452 }
453 
454 bool SessionPrivate::validateSessionId(const QString &id)
455 {
456  auto it = id.constBegin();
457  auto end = id.constEnd();
458  while (it != end) {
459  QChar c = *it;
460  if ((c >= u'a' && c <= u'f') || (c >= u'0' && c <= u'9')) {
461  ++it;
462  continue;
463  }
464  return false;
465  }
466 
467  return id.size();
468 }
469 
470 qint64 SessionPrivate::extendSessionExpires(Session *session, Context *c, qint64 expires)
471 {
472  const qint64 threshold = qint64(session->d_ptr->expiryThreshold);
473 
474  const QString sid = Session::id(c);
475  if (!sid.isEmpty()) {
476  const qint64 current = getStoredSessionExpires(session, c, sid);
477  const qint64 cutoff = current - threshold;
478  const qint64 time = QDateTime::currentMSecsSinceEpoch() / 1000;
479 
480  if (!threshold || cutoff <= time || c->stash(SESSION_UPDATED).toBool()) {
481  qint64 updated = calculateInitialSessionExpires(session, c, sid);
482  c->setStash(SESSION_EXTENDED_EXPIRES, updated);
483  extendSessionId(session, c, sid, updated);
484 
485  return updated;
486  } else {
487  return current;
488  }
489  } else {
490  return expires;
491  }
492 }
493 
494 qint64
495  SessionPrivate::getStoredSessionExpires(Session *session, Context *c, const QString &sessionid)
496 {
497  const QVariant expires =
498  session->d_ptr->store->getSessionData(c, sessionid, QStringLiteral("expires"), 0);
499  return expires.toLongLong();
500 }
501 
502 QVariant SessionPrivate::initializeSessionData(Session *session, Context *c)
503 {
504  QVariantHash ret;
505  const qint64 now = QDateTime::currentMSecsSinceEpoch() / 1000;
506  ret.insert(QStringLiteral("__created"), now);
507  ret.insert(QStringLiteral("__updated"), now);
508 
509  if (session->d_ptr->verifyAddress) {
510  ret.insert(QStringLiteral("__address"), c->request()->address().toString());
511  }
512 
513  if (session->d_ptr->verifyUserAgent) {
514  ret.insert(QStringLiteral("__user_agent"), c->request()->userAgent());
515  }
516 
517  return ret;
518 }
519 
520 void SessionPrivate::saveSessionExpires(Context *c)
521 {
522  const QVariant expires = c->stash(SESSION_EXPIRES);
523  if (!expires.isNull()) {
524  const QString sid = Session::id(c);
525  if (!sid.isEmpty()) {
526  if (Q_UNLIKELY(!m_instance)) {
527  qCCritical(C_SESSION) << "Session plugin not registered";
528  return;
529  }
530 
531  const qint64 current = getStoredSessionExpires(m_instance, c, sid);
532  const qint64 extended = qint64(Session::expires(c));
533  if (extended > current) {
534  m_instance->d_ptr->store->storeSessionData(
535  c, sid, QStringLiteral("expires"), extended);
536  }
537  }
538  }
539 }
540 
541 QVariant SessionPrivate::loadSessionExpires(Session *session, Context *c, const QString &sessionId)
542 {
543  QVariant ret;
544  if (c->stash(SESSION_TRIED_LOADING_EXPIRES).toBool()) {
545  ret = c->stash(SESSION_EXPIRES);
546  return ret;
547  }
548  c->setStash(SESSION_TRIED_LOADING_EXPIRES, true);
549 
550  if (!sessionId.isEmpty()) {
551  const qint64 expires = getStoredSessionExpires(session, c, sessionId);
552 
553  if (expires >= QDateTime::currentMSecsSinceEpoch() / 1000) {
554  c->setStash(SESSION_EXPIRES, expires);
555  ret = expires;
556  } else {
557  deleteSession(session, c, QStringLiteral("session expired"));
558  ret = 0;
559  }
560  }
561  return ret;
562 }
563 
564 qint64 SessionPrivate::initialSessionExpires(Session *session, Context *c)
565 {
566  Q_UNUSED(c)
567  const qint64 expires = qint64(session->d_ptr->sessionExpires);
568  return QDateTime::currentMSecsSinceEpoch() / 1000 + expires;
569 }
570 
571 qint64 SessionPrivate::calculateInitialSessionExpires(Session *session,
572  Context *c,
573  const QString &sessionId)
574 {
575  const qint64 stored = getStoredSessionExpires(session, c, sessionId);
576  const qint64 initial = initialSessionExpires(session, c);
577  return qMax(initial, stored);
578 }
579 
580 qint64 SessionPrivate::resetSessionExpires(Session *session, Context *c, const QString &sessionId)
581 {
582  const qint64 exp = calculateInitialSessionExpires(session, c, sessionId);
583 
584  c->setStash(SESSION_EXPIRES, exp);
585 
586  // since we're setting _session_expires directly, make loadSessionExpires
587  // actually use that value.
588  c->setStash(SESSION_TRIED_LOADING_EXPIRES, true);
589  c->setStash(SESSION_EXTENDED_EXPIRES, exp);
590 
591  return exp;
592 }
593 
594 void SessionPrivate::updateSessionCookie(Context *c, const QNetworkCookie &updated)
595 {
596  c->response()->setCookie(updated);
597 }
598 
599 #if (QT_VERSION < QT_VERSION_CHECK(6, 1, 0))
600 void SessionPrivate::updateSessionCuteCookie(Context *c, const Cookie &updated)
601 {
602  c->response()->setCuteCookie(updated);
603 }
604 #endif
605 
606 QNetworkCookie SessionPrivate::makeSessionCookie(Session *session,
607  Context *c,
608  const QString &sid,
609  const QDateTime &expires)
610 {
611  Q_UNUSED(c)
612  QNetworkCookie cookie(session->d_ptr->sessionName.toLatin1(), sid.toLatin1());
613  cookie.setPath(QStringLiteral("/"));
614  cookie.setExpirationDate(expires);
615  cookie.setHttpOnly(session->d_ptr->cookieHttpOnly);
616  cookie.setSecure(session->d_ptr->cookieSecure);
617 #if (QT_VERSION >= QT_VERSION_CHECK(6, 1, 0))
618  cookie.setSameSitePolicy(session->d_ptr->cookieSameSite);
619 #endif
620 
621  return cookie;
622 }
623 
624 #if (QT_VERSION < QT_VERSION_CHECK(6, 1, 0))
625 Cookie SessionPrivate::makeSessionCuteCookie(Session *session,
626  Context *c,
627  const QString &sid,
628  const QDateTime &expires)
629 {
630  Q_UNUSED(c)
631  Cookie cookie(session->d_ptr->sessionName.toLatin1(), sid.toLatin1());
632  cookie.setPath(QStringLiteral("/"));
633  cookie.setExpirationDate(expires);
634  cookie.setHttpOnly(session->d_ptr->cookieHttpOnly);
635  cookie.setSecure(session->d_ptr->cookieSecure);
636  cookie.setSameSitePolicy(session->d_ptr->cookieSameSite);
637 
638  return cookie;
639 }
640 #endif
641 
642 void SessionPrivate::extendSessionId(Session *session,
643  Context *c,
644  const QString &sid,
645  qint64 expires)
646 {
647 #if (QT_VERSION >= QT_VERSION_CHECK(6, 1, 0))
648  updateSessionCookie(
649  c, makeSessionCookie(session, c, sid, QDateTime::fromMSecsSinceEpoch(expires * 1000)));
650 #else
651  updateSessionCuteCookie(
652  c, makeSessionCuteCookie(session, c, sid, QDateTime::fromMSecsSinceEpoch(expires * 1000)));
653 #endif
654 }
655 
656 void SessionPrivate::setSessionId(Session *session, Context *c, const QString &sid)
657 {
658 #if (QT_VERSION >= QT_VERSION_CHECK(6, 1, 0))
659  updateSessionCookie(c,
660  makeSessionCookie(session,
661  c,
662  sid,
664  initialSessionExpires(session, c) * 1000)));
665 #else
666  updateSessionCuteCookie(c,
667  makeSessionCuteCookie(session,
668  c,
669  sid,
671  initialSessionExpires(session, c) * 1000)));
672 #endif
673 }
674 
676  : QObject(parent)
677 {
678 }
679 
680 #include "moc_session.cpp"
qlonglong toLongLong(bool *ok) const const
void setCookie(const QNetworkCookie &cookie)
Definition: response.cpp:232
QHash< QString, QVariant > toHash() const const
void postForked(Cutelyst::Application *app)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void setStash(const QString &key, const QVariant &value)
Definition: context.cpp:217
QString toString() const const
qint64 currentMSecsSinceEpoch()
The Cutelyst Context.
Definition: context.h:38
QHostAddress address() const noexcept
Definition: request.cpp:33
static quint64 expires(Context *c)
Definition: session.cpp:123
bool isNull() const const
void stash(const QVariantHash &unite)
Definition: context.cpp:566
CaseInsensitive
QVariantMap config(const QString &entity) const
user configuration for the application
Definition: engine.cpp:290
bool isEmpty() const const
static bool isValid(Context *c)
Definition: session.cpp:259
static void deleteSession(Context *c, const QString &reason=QString())
Definition: session.cpp:156
static void setValue(Context *c, const QString &key, const QVariant &value)
Definition: session.cpp:185
QDateTime fromMSecsSinceEpoch(qint64 msecs)
void setStorage(SessionStore *store)
Definition: session.cpp:91
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
Session(Application *parent)
Definition: session.cpp:34
void setParent(QObject *parent)
SessionStore * storage() const
Definition: session.cpp:99
static QString deleteReason(Context *c)
Definition: session.cpp:165
static void deleteValue(Context *c, const QString &key)
Definition: session.cpp:209
QString fromLatin1(QByteArrayView str)
void afterDispatch(Cutelyst::Context *c)
QByteArray toLatin1() const const
virtual bool storeSessionData(Context *c, const QString &sid, const QString &key, const QVariant &value)=0
static void deleteValues(Context *c, const QStringList &keys)
Definition: session.cpp:233
static QVariant value(Context *c, const QString &key, const QVariant &defaultValue=QVariant())
Definition: session.cpp:170
virtual bool setup(Application *app) final
Definition: session.cpp:45
QString cookie(const QString &name) const
Definition: request.cpp:274
The Cutelyst Application.
Definition: application.h:42
SessionStore(QObject *parent=nullptr)
Definition: session.cpp:675
Engine * engine() const noexcept
Response * response() const noexcept
Definition: context.cpp:96
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
QString toString() const const
QDateTime currentDateTimeUtc()
The Cutelyst Cookie.
Definition: cookie.h:28
static QString id(Context *c)
Definition: session.cpp:105
QUuid createUuid()
static void changeExpires(Context *c, quint64 expires)
Definition: session.cpp:143
QString applicationName()