Cutelyst  1.12.0
sessionstorememcached.cpp
1 /*
2  * Copyright (C) 2017 Matthias Fehring <kontakt@buschmann23.de>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 #include "sessionstorememcached_p.h"
19 
20 #include <Cutelyst/Context>
21 
22 #include <QLoggingCategory>
23 #include <QDataStream>
24 #include <QCoreApplication>
25 
26 using namespace Cutelyst;
27 
28 Q_LOGGING_CATEGORY(C_SESSION_MEMCACHED, "cutelyst.plugin.sessionmemcached")
29 
30 #define SESSION_STORE_MEMCD_SAVE "__session_store_memcd_save"
31 #define SESSION_STORE_MEMCD_DATA "__session_store_memcd_data"
32 
33 static QVariantHash loadMemcSessionData(Context *c, const QString &sid, const std::string &config);
34 
35 SessionStoreMemcached::SessionStoreMemcached(QObject *parent) :
36  SessionStore(parent), d_ptr(new SessionStoreMemcachedPrivate)
37 {
38 
39 }
40 
41 SessionStoreMemcached::SessionStoreMemcached(const QString &config, QObject *parent) :
42  SessionStore(parent), d_ptr(new SessionStoreMemcachedPrivate(config))
43 {
44 
45 }
46 
48 {
49  delete d_ptr;
50 }
51 
52 QVariant SessionStoreMemcached::getSessionData(Context *c, const QString &sid, const QString &key, const QVariant &defaultValue)
53 {
54  const QVariantHash data = loadMemcSessionData(c, sid, d_ptr->config);
55 
56  return data.value(key, defaultValue);
57 }
58 
59 bool SessionStoreMemcached::storeSessionData(Context *c, const QString &sid, const QString &key, const QVariant &value)
60 {
61  QVariantHash data = loadMemcSessionData(c, sid, d_ptr->config);
62 
63  data.insert(key, value);
64  c->setProperty(SESSION_STORE_MEMCD_DATA, data);
65  c->setProperty(SESSION_STORE_MEMCD_SAVE, true);
66 
67  return true;
68 }
69 
70 bool SessionStoreMemcached::deleteSessionData(Context *c, const QString &sid, const QString &key)
71 {
72  QVariantHash data = loadMemcSessionData(c, sid, d_ptr->config);
73 
74  data.remove(key);
75  c->setProperty(SESSION_STORE_MEMCD_DATA, data);
76  c->setProperty(SESSION_STORE_MEMCD_SAVE, true);
77 
78  return true;
79 }
80 
82 {
83  Q_UNUSED(c)
84  Q_UNUSED(expires)
85  return true;
86 }
87 
88 QVariantHash loadMemcSessionData(Context *c, const QString &sid, const std::string &config)
89 {
90  QVariantHash data;
91  const QVariant sessionVariant = c->property(SESSION_STORE_MEMCD_DATA);
92  if (!sessionVariant.isNull()) {
93  data = sessionVariant.toHash();
94  return data;
95  }
96 
97  const static QString sessionPrefix = QCoreApplication::applicationName() + QLatin1String("_sess_");
98  const QString sessionKey = sessionPrefix + sid;
99 
100  static memcache::Memcache memc(config);
101 
102  QObject::connect(c, &Context::destroyed, [=] () {
103  if (!c->property(SESSION_STORE_MEMCD_SAVE).toBool()) {
104  return;
105  }
106 
107  QVariantHash data = c->property(SESSION_STORE_MEMCD_DATA).toHash();
108 
109  if (data.isEmpty()) {
110  if (!memc.remove(sessionKey.toStdString())) {
111  std::string errorString;
112  memc.error(errorString);
113  qCWarning(C_SESSION_MEMCACHED) << "Failed to remove session from Memcached:" << QString::fromStdString(errorString);
114  }
115  } else {
116  QByteArray storeData;
117  QDataStream out(&storeData, QIODevice::WriteOnly);
118  out << data;
119 
120  if (!memc.set(sessionKey.toStdString(),
121  storeData.data(),
122  storeData.size(),
123  data.value(QStringLiteral("expires")).toUInt(),
124  (uint32_t)0))
125  {
126  std::string errorString;
127  memc.error(errorString);
128  qCWarning(C_SESSION_MEMCACHED) << "Failed to store session to Memcached:" << QString::fromStdString(errorString);
129  }
130  }
131  });
132 
133  std::vector<char> storedData;
134  if (memc.get(sessionKey.toStdString(), storedData)) {
135  if (!storedData.empty()) {
136  QByteArray storedArray;
137  for (auto it = storedData.cbegin(); it != storedData.cend(); ++it) {
138  storedArray.append(*it);
139  }
140  if (!storedArray.isNull()) {
141  QDataStream in(&storedArray, QIODevice::ReadOnly);
142  in >> data;
143  }
144  }
145  }
146 
147  c->setProperty(SESSION_STORE_MEMCD_DATA, data);
148 
149  return data;
150 }
~SessionStoreMemcached()
Deconstructs the SessionStoreMemcached object.
bool error() const
Returns true if an error was set.
Definition: context.cpp:64
virtual bool storeSessionData(Context *c, const QString &sid, const QString &key, const QVariant &value) final
The Cutelyst Context.
Definition: context.h:50
virtual bool deleteExpiredSessions(Context *c, quint64 expires) final
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
virtual QVariant getSessionData(Context *c, const QString &sid, const QString &key, const QVariant &defaultValue) final
virtual bool deleteSessionData(Context *c, const QString &sid, const QString &key) final