41 unordered_map<string, any> data;
43 const string sourceIP;
48 SessionData() : expires(0) {}
54 explicit SessionData(
string sIP) : expires(0), sourceIP(std::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);
104struct 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);
127std::string Session::start(std::string sessionId, std::optional<unsigned long> keepalive) {
130 return data->currentID;
134 unsigned long sessionKeepalive = 1800;
136 sessionKeepalive = *keepalive;
138 auto sessionKStr = data->connection.config()[{
"session",
"keepalive"}];
139 if (!sessionKStr.empty()) {
141 sessionKeepalive = stoul(sessionKStr);
142 }
catch (invalid_argument& e) {
143 sessionKeepalive = 1800;
148 if (!sessionId.empty()) {
151 lock_guard<mutex> lockGuard(gLock);
152 if (sessionData.count(sessionId) == 1) {
154 auto sessionValidateIP = data->connection.config()[{
"session",
"validate_ip"}];
156 if (sessionData.at(sessionId)->expires <= time(
nullptr)) {
157 sessionData.erase(sessionId);
160 else if ((sessionValidateIP ==
"strict" || sessionValidateIP ==
"lax") &&
161 sessionData.at(sessionId)->sourceIP != data->connection.request().env()[
"REMOTE_ADDR"]) {
162 if (sessionValidateIP ==
"strict") {
164 sessionData.erase(sessionId);
169 data->currentData = sessionData.at(sessionId);
171 lock_guard<mutex> currentLock(data->currentData->eLock);
172 data->currentData->expires = time(
nullptr) + sessionKeepalive;
177 if (data->currentData.use_count() < 1) {
179 lock_guard<mutex> lockGuard(gLock);
181 sessionId = generateID(data->connection.request().env()[
"REMOTE_ADDR"]);
182 }
while (sessionData.count(sessionId) > 0);
183 data->currentData = make_shared<SessionData>(data->connection.request().env()[
"REMOTE_ADDR"]);
184 data->currentData->expires = time(
nullptr) + sessionKeepalive;
185 sessionData[sessionId] = data->currentData;
189 data->currentID = sessionId;
192 unsigned long divisor;
194 auto divisorStr = data->connection.config()[{
"session",
"gc_divisor"}];
195 if (!divisorStr.empty()) {
196 divisor = stoul(divisorStr);
200 }
catch (invalid_argument
const& e) {
204 if (rd() % divisor == 0) {
217 data->cookieName = data->connection.config()[{
"session",
"cookie_name"}];
218 if (data->cookieName.empty()) {
219 data->cookieName =
"SESSION";
223 unsigned long sessionKeepalive = 1800;
224 if (properties.
maxAge()) {
225 sessionKeepalive = *properties.
maxAge();
227 auto sessionKStr = data->connection.config()[{
"session",
"keepalive"}];
228 if (!sessionKStr.empty()) {
230 sessionKeepalive = stoul(sessionKStr);
231 }
catch (invalid_argument& e) {
232 sessionKeepalive = 1800;
239 string sessionId =
start(data->connection.request().cookie()[data->cookieName], sessionKeepalive);
242 string cookieExpiresStr;
243 if (properties.
expires() || data->connection.config()[{
"session",
"cookie_expires"}] !=
"off") {
244 properties.
expires(time(
nullptr) + sessionKeepalive)
245 .maxAge(sessionKeepalive);
248 properties.
maxAge(nullopt);
251 if (!properties.
secure() && data->connection.config()[{
"session",
"cookie_secure"}] !=
"off") {
254 if (!properties.
httpOnly() && data->connection.config()[{
"session",
"cookie_httponly"}] !=
"off") {
258 auto sessionSameSite = data->connection.config()[{
"session",
"cookie_samesite"}];
259 if (sessionSameSite ==
"lax") {
261 }
else if (sessionSameSite !=
"off") {
267 data->currentID = sessionId;
271 data->connection.setCookie(data->cookieName, properties);
275 return (data->currentData.use_count() > 0);
280 lock_guard<mutex> lockGuard(data->currentData->dLock);
281 return (data->currentData->data.count(key) == 1);
288 lock_guard<mutex> lockGuard(data->currentData->dLock);
289 if (data->currentData->data.count(key) == 1) {
290 return data->currentData->data.at(key);
299 throw Exception(__PRETTY_FUNCTION__, 1,
"Session not established.");
301 lock_guard<mutex> lockGuard(data->currentData->dLock);
302 data->currentData->data[std::move(key)] = value;
307 throw Exception(__PRETTY_FUNCTION__, 1,
"Session not established.");
309 lock_guard<mutex> lockGuard(data->currentData->dLock);
310 data->currentData->data.erase(key);
320 data->currentData.reset();
324 lock_guard<mutex> lockGuard(gLock);
325 sessionData.erase(data->currentID);
329 data->connection.unsetCookie(data->cookieName);
336void 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.
std::optional< time_t > & expires() noexcept
SameSite & sameSite() noexcept
std::string & content() noexcept
bool & httpOnly() noexcept
std::optional< unsigned long > & maxAge() noexcept
std::any operator[](std::string const &key) const
void set(std::string key, const std::any &value)
std::string start(std::string sessionId, std::optional< unsigned long > keepalive=std::nullopt)
std::string getID() 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)