41 unordered_map<string, any> data;
43 const string sourceIP;
47 SessionData() : expires(0) {}
53 explicit SessionData(
string sIP) : expires(0), sourceIP(move(sIP)) {}
60 unordered_map<string, shared_ptr<SessionData>> sessionData;
67 string generateID(
string const& remoteAddress) {
75 base << remoteAddress;
85 void collectGarbage() {
86 lock_guard<mutex> lockGuard(gLock);
88 for (
auto it = sessionData.cbegin(); it != sessionData.cend();) {
89 bool toDelete =
false;
91 lock_guard<mutex> eGuard(it->second->eLock);
92 toDelete = (it->second->expires < time(
nullptr));
95 it = sessionData.erase(it);
103struct Session::Data {
110 std::shared_ptr<SessionData> currentData;
111 std::string currentID;
112 std::string cookieName;
114 explicit Data(
Connection& connection) : connection(connection) {}
120 data = make_unique<Data>(connection);
133 data->cookieName = data->connection.config()[{
"session",
"cookie_name"}];
134 if (data->cookieName.empty()) {
135 data->cookieName =
"SESSION";
139 unsigned long sessionKeepalive = 1800;
140 if (properties.
maxAge()) {
141 sessionKeepalive = *properties.
maxAge();
143 auto sessionKStr = data->connection.config()[{
"session",
"keepalive"}];
144 if (!sessionKStr.empty()) {
146 sessionKeepalive = stoul(sessionKStr);
147 }
catch (invalid_argument& e) {
148 sessionKeepalive = 1800;
154 auto sessionCookieStr = data->connection.request().cookie()[data->cookieName];
155 if (!sessionCookieStr.empty()) {
158 lock_guard<mutex> lockGuard(gLock);
159 if (sessionData.count(sessionCookieStr) == 1) {
161 auto sessionValidateIP = data->connection.config()[{
"session",
"validate_ip"}];
163 if (sessionData.at(sessionCookieStr)->expires <= time(
nullptr)) {
164 sessionData.erase(sessionCookieStr);
167 else if ((sessionValidateIP ==
"strict" || sessionValidateIP ==
"lax") &&
168 sessionData.at(sessionCookieStr)->sourceIP != data->connection.request().env()[
"REMOTE_ADDR"]) {
169 if (sessionValidateIP ==
"strict") {
171 sessionData.erase(sessionCookieStr);
176 data->currentData = sessionData.at(sessionCookieStr);
178 lock_guard<mutex> currentLock(data->currentData->eLock);
179 data->currentData->expires = time(
nullptr) + sessionKeepalive;
184 if (data->currentData.use_count() < 1) {
186 lock_guard<mutex> lockGuard(gLock);
188 sessionCookieStr = generateID(data->connection.request().env()[
"REMOTE_ADDR"]);
189 }
while (sessionData.count(sessionCookieStr) > 0);
190 data->currentData = make_shared<SessionData>(data->connection.request().env()[
"REMOTE_ADDR"]);
191 data->currentData->expires = time(
nullptr) + sessionKeepalive;
192 sessionData[sessionCookieStr] = data->currentData;
196 string cookieExpiresStr;
197 if (properties.
expires() || data->connection.config()[{
"session",
"cookie_expires"}] !=
"off") {
198 properties.
expires(time(
nullptr) + sessionKeepalive)
199 .maxAge(sessionKeepalive);
202 properties.
maxAge(nullopt);
205 if (!properties.
secure() && data->connection.config()[{
"session",
"cookie_secure"}] !=
"off") {
208 if (!properties.
httpOnly() && data->connection.config()[{
"session",
"cookie_httponly"}] !=
"off") {
212 auto sessionSameSite = data->connection.config()[{
"session",
"cookie_samesite"}];
213 if (sessionSameSite ==
"lax") {
215 }
else if (sessionSameSite !=
"off") {
221 data->currentID = sessionCookieStr;
224 properties.
content(sessionCookieStr);
225 data->connection.setCookie(data->cookieName, properties);
228 unsigned long divisor;
230 auto divisorStr = data->connection.config()[{
"session",
"gc_divisor"}];
231 if (!divisorStr.empty()) {
232 divisor = stoul(divisorStr);
236 }
catch (invalid_argument
const& e) {
240 if (rd() % divisor == 0) {
246 return (data->currentData.use_count() > 0);
251 lock_guard<mutex> lockGuard(data->currentData->dLock);
252 return (data->currentData->data.count(key) == 1);
259 lock_guard<mutex> lockGuard(data->currentData->dLock);
260 if (data->currentData->data.count(key) == 1) {
261 return data->currentData->data.at(key);
270 throw Exception(__PRETTY_FUNCTION__, 1,
"Session not established.");
272 lock_guard<mutex> lockGuard(data->currentData->dLock);
273 data->currentData->data[move(key)] = value;
278 throw Exception(__PRETTY_FUNCTION__, 1,
"Session not established.");
280 lock_guard<mutex> lockGuard(data->currentData->dLock);
281 data->currentData->data.erase(key);
291 data->currentData.reset();
295 lock_guard<mutex> lockGuard(gLock);
296 sessionData.erase(data->currentID);
300 data->connection.unsetCookie(data->cookieName);
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.
std::optional< time_t > & expires() noexcept
SameSite & sameSite() noexcept
std::string & content() noexcept
bool & httpOnly() noexcept
std::optional< unsigned long > & maxAge() noexcept
std::string getID() const
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)