-
-
Notifications
You must be signed in to change notification settings - Fork 365
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sessions Middleware #448
Sessions Middleware #448
Conversation
--- include/crow/middlewares/cookie_parser.h (before formatting)
+++ include/crow/middlewares/cookie_parser.h (after formatting)
@@ -50,7 +50,9 @@
value_ = std::forward<U>(value);
}
- Cookie(const std::string& key): Cookie() {
+ Cookie(const std::string& key):
+ Cookie()
+ {
key_ = key;
}
@@ -94,12 +96,14 @@
return ss.str();
}
- const std::string& name() {
+ const std::string& name()
+ {
return key_;
}
template<typename U>
- void value(U&& value) {
+ void value(U&& value)
+ {
value_ = std::forward<U>(value);
}
@@ -216,7 +220,8 @@
return cookies_to_add.back();
}
- void set_cookie(Cookie cookie) {
+ void set_cookie(Cookie cookie)
+ {
cookies_to_add.push_back(std::move(cookie));
}
|
I'd say this is ready to have a look at and experiment with. You can also check the examples or run the small demo I left in gitter. |
@dranikpg I should mention regarding the unittest you added, @luca-schlecker is working on separating the unit tests into multiple source files so I'm just letting you know in case his PR gets merged before this one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have much other than the comments, everything seems alright. Although I would like a second opinion since the added code is rather large.
I've stripped out custom keys. Even if this feature might be handy in certain cases, using it correctly is quite tricky. Supporting it really-really securely would require adding another secure hasing algorithm more powerfult than sha1. |
--- include/crow/middlewares/cookie_parser.h (before formatting)
+++ include/crow/middlewares/cookie_parser.h (after formatting)
@@ -51,7 +51,7 @@
}
Cookie(const std::string& key):
- Cookie(key, ""){}
+ Cookie(key, "") {}
// format cookie to HTTP header format
std::string dump() const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent work. 👍
Except for the comments, this is looking quite ready.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. 🚀
Merging now, Thank you @dranikpg for all the work you've done and your patience during the review process! |
Sessions
This is the main implementation for #406 . I ended up focusing on backend storage only, making the design less flexible, but easier to use and implement.
What made it into this version
* Session keys are stored in cookies and are signed with a HMAC (Hash-based message authentication code), so custom keys and/or short keys are safe to use.What it looks like
How it works
1. Cookies.
Each client stores its session id in the cookies. Each id is signed, so that it can be changed only by the server. The Session middleware reads the cookie in
read_id
and verifies the signature.2. Session cache.
If a valid session id was found, the middleware looks in the cache for a
CachedSession
. The cache holds all currently active session objects. ACachedSession
contains not only data, but also a mutex for locking, its id, a list of keys that changed since last load and some auxiliary values. Because of the cache, all concurrent requests share literally the same object, so no "data race" can happen and all modifications are instantly visible to all requests.3. Stores.
If no
CachedSession
was found, it has to be loaded from the store and put into the cache. First the SessionMiddleware checks for validity withcontains()
and then callsload()
on the store. As soon as there are no requests referring to aCachedSession
, it is released back into the store and saved.4. Fresh sessions.
Sessions are initialized on first write (no need to expiclitly create one for the user) and have no id until they are persisted.
If no special id was requested (viaa random id will be generated and stored in the clients cookies.preset_id
),Its imporant to understand, that session initialization does not work with concurrent requests, because the client receives it cookies only with the response. Only requests that already have a valid session will share one session object.
5. Expiration.
It'd be silly to store all data forever. That's why the FileStore deletes files after X seconds (by default a whole month). When the file is gone, the session will no longer be valid and a new one will be issued.
Cookies are ought to expire as well. By default they're valid for one month.
The expiration stamps can be prolonged by calling
refresh_expiration()
on a session object. This will make the middleware issue a new cookie and the store to postpone its deletions.6. Multi-typed map.
Session data is represented as a
unordered_map<string, mutli_value>
. Amulti_value
allows storing strings, integers, doubles and booleans. Because dealing with multiple integer types would be cumbersome, it "promotes" all integer values to int64_t. Besides, it has helpers to convert the value from/to json and represent it as a string.On C++17 and above
multi_value
is implemented on top ofstd::variant
, otherwise it usesjson::wvalue
with some quirks for reading the value back.7. Default stores. The
InMemoryStore
is really simple, it stores all entries just in a hashmap.FileStore
is a little bit more complex. It stores data in json files and keeps track of soon-to-expire keys. Generally, a custom store can be implemented quite easily.Current issues
Expiration trackingAdded general purpose ExpirationTracker that is used by the FileStoreThere is no alternative for the C++17 variant (as Crow is now deboostified) for C++14/11Solved with a crooked wrapper around json::wvalue