41 unordered_map<string, any> data;
43 const string sourceIP;
48 SessionData() : expires(0) {}
54 explicit SessionData(
string sIP) : expires(0), sourceIP(move(sIP)) {}
61 unordered_map<string, shared_ptr<SessionData>> sessionData;
68 string generateID(
string const& remoteAddress) {
76 base << remoteAddress;
86 void collectGarbage() {
87 lock_guard<mutex> lockGuard(gLock);
89 for (
auto it = sessionData.cbegin(); it != sessionData.cend();) {
90 bool toDelete =
false;
92 lock_guard<mutex> eGuard(it->second->eLock);
93 toDelete = (it->second->expires < time(
nullptr));
96 it = sessionData.erase(it);
104 struct Session::Data {
111 std::shared_ptr<SessionData> currentData;
112 std::string currentID;
113 std::string cookieName;
115 explicit Data(
Connection& connection) : connection(connection) {}
121 data = make_unique<Data>(connection);
134 data->cookieName = data->connection.config()[{
"session",
"cookie_name"}];
135 if (data->cookieName.empty()) {
136 data->cookieName =
"SESSION";
140 unsigned long sessionKeepalive = 1800;
141 if (properties.
maxAge()) {
142 sessionKeepalive = *properties.
maxAge();
144 auto sessionKStr = data->connection.config()[{
"session",
"keepalive"}];
145 if (!sessionKStr.empty()) {
147 sessionKeepalive = stoul(sessionKStr);
148 }
catch (invalid_argument& e) {
149 sessionKeepalive = 1800;
155 auto sessionCookieStr = data->connection.request().cookie()[data->cookieName];
156 if (!sessionCookieStr.empty()) {
159 lock_guard<mutex> lockGuard(gLock);
160 if (sessionData.count(sessionCookieStr) == 1) {
162 auto sessionValidateIP = data->connection.config()[{
"session",
"validate_ip"}];
164 if (sessionData.at(sessionCookieStr)->expires <= time(
nullptr)) {
165 sessionData.erase(sessionCookieStr);
168 else if ((sessionValidateIP ==
"strict" || sessionValidateIP ==
"lax") &&
169 sessionData.at(sessionCookieStr)->sourceIP != data->connection.request().env()[
"REMOTE_ADDR"]) {
170 if (sessionValidateIP ==
"strict") {
172 sessionData.erase(sessionCookieStr);
177 data->currentData = sessionData.at(sessionCookieStr);
179 lock_guard<mutex> currentLock(data->currentData->eLock);
180 data->currentData->expires = time(
nullptr) + sessionKeepalive;
185 if (data->currentData.use_count() < 1) {
187 lock_guard<mutex> lockGuard(gLock);
189 sessionCookieStr = generateID(data->connection.request().env()[
"REMOTE_ADDR"]);
190 }
while (sessionData.count(sessionCookieStr) > 0);
191 data->currentData = make_shared<SessionData>(data->connection.request().env()[
"REMOTE_ADDR"]);
192 data->currentData->expires = time(
nullptr) + sessionKeepalive;
193 sessionData[sessionCookieStr] = data->currentData;
197 string cookieExpiresStr;
198 if (properties.
expires() || data->connection.config()[{
"session",
"cookie_expires"}] !=
"off") {
199 properties.
expires(time(
nullptr) + sessionKeepalive)
200 .maxAge(sessionKeepalive);
203 properties.
maxAge(nullopt);
206 if (!properties.
secure() && data->connection.config()[{
"session",
"cookie_secure"}] !=
"off") {
209 if (!properties.
httpOnly() && data->connection.config()[{
"session",
"cookie_httponly"}] !=
"off") {
213 auto sessionSameSite = data->connection.config()[{
"session",
"cookie_samesite"}];
214 if (sessionSameSite ==
"lax") {
216 }
else if (sessionSameSite !=
"off") {
222 data->currentID = sessionCookieStr;
225 properties.
content(sessionCookieStr);
226 data->connection.setCookie(data->cookieName, properties);
229 unsigned long divisor;
231 auto divisorStr = data->connection.config()[{
"session",
"gc_divisor"}];
232 if (!divisorStr.empty()) {
233 divisor = stoul(divisorStr);
237 }
catch (invalid_argument
const& e) {
241 if (rd() % divisor == 0) {
247 return (data->currentData.use_count() > 0);
252 lock_guard<mutex> lockGuard(data->currentData->dLock);
253 return (data->currentData->data.count(key) == 1);
260 lock_guard<mutex> lockGuard(data->currentData->dLock);
261 if (data->currentData->data.count(key) == 1) {
262 return data->currentData->data.at(key);
270 if (!established()) {
271 throw Exception(__PRETTY_FUNCTION__, 1,
"Session not established.");
273 lock_guard<mutex> lockGuard(data->currentData->dLock);
274 data->currentData->data[move(key)] = value;
278 if (!established()) {
279 throw Exception(__PRETTY_FUNCTION__, 1,
"Session not established.");
281 lock_guard<mutex> lockGuard(data->currentData->dLock);
282 data->currentData->data.erase(key);
292 data->currentData.reset();
296 lock_guard<mutex> lockGuard(gLock);
297 sessionData.erase(data->currentID);
301 data->connection.unsetCookie(data->cookieName);
305 return established() ? data->currentID : string();
308 void Session::destroy() {
Response object to be passed back to NAWA and accessor to the request.
Exception class that can be used by apps to catch errors resulting from nawa function calls.
Class for managing sessions and getting and setting connection-independent session data.
SameSite & sameSite() noexcept
std::string & content() noexcept
bool & httpOnly() noexcept
std::optional< time_t > & expires() noexcept
std::optional< unsigned long > & maxAge() noexcept
std::string getID() const
void start(nawa::Cookie properties=Cookie())
void set(std::string key, const std::any &value)
std::any operator[](std::string const &key) const
void unset(std::string const &key)
bool isSet(std::string const &key) const
A bunch of useful cryptographic functions (esp. hashing), acting as a wrapper to C crypto libraries.
#define NAWA_DEFAULT_DESTRUCTOR_IMPL(Class)
std::string sha1(std::string const &input, bool hex=true)