Added a option to use non-cookies based session ID storage Tests not implemented yetmaster
@@ -17,18 +17,24 @@ | |||
#include <cppcms/serialization_classes.h> | |||
#include <string> | |||
#include <map> | |||
#include <set> | |||
#include <memory> | |||
#include <sstream> | |||
#include <typeinfo> | |||
namespace cppcms { | |||
namespace impl { | |||
struct cached_settings; | |||
} | |||
namespace http { | |||
class context; | |||
class request; | |||
class response; | |||
class cookie; | |||
} | |||
class session_api; | |||
class session_pool; | |||
/// | |||
/// \brief This exception is thrown when CSRF attempt is suspected: | |||
@@ -42,6 +48,34 @@ public: | |||
} | |||
}; | |||
/// | |||
/// API to handle session cookies. | |||
/// | |||
/// This API allows two things: | |||
/// | |||
/// (a) Integration with 3rd part web technologies to access CppCMS session, i.e. using CppCMS session | |||
/// from PHP or Java Servlets | |||
/// | |||
/// (b) An API that allows to translate cookies session tracking system to a different | |||
/// method when cookies do not suite the design - for example for internal RPC | |||
/// systems, etc. Note incorrect use of non-cookies medium may expose you | |||
/// to security issues | |||
/// | |||
class CPPCMS_API session_interface_cookie_adapter : public booster::noncopyable { | |||
public: | |||
virtual ~session_interface_cookie_adapter(); | |||
/// | |||
/// Set a new cookie value | |||
/// | |||
virtual void set_cookie(http::cookie const &updated_cookie) = 0; | |||
/// | |||
/// Get value of the cookie, it is guaranteed that \a name is | |||
/// what session_interface::session_cookie_name() returns | |||
/// | |||
virtual std::string get_session_cookie(std::string const &name) = 0; | |||
}; | |||
/// | |||
/// \brief This class provides an access to an application for session management | |||
/// | |||
@@ -69,12 +103,23 @@ public: | |||
class CPPCMS_API session_interface : private booster::noncopyable { | |||
public: | |||
/// \cond INTERNAL | |||
/// | |||
/// Create cppcms::service independent session interface to be used | |||
/// for implementing interoperability with non-cppcms based web platforms | |||
/// | |||
session_interface(session_pool &pool,session_interface_cookie_adapter &adapter); | |||
/// | |||
/// Creates session interface for the context - never should be used by users | |||
/// directly | |||
/// | |||
session_interface(http::context &); | |||
~session_interface(); | |||
/// \endcond | |||
/// | |||
/// destructor... | |||
/// | |||
~session_interface(); | |||
/// | |||
/// Check if a \a key is set (assigned some value to it) in the session | |||
/// | |||
bool is_set(std::string const &key); | |||
@@ -268,6 +313,14 @@ public: | |||
bool load(); | |||
/// | |||
/// Set alternative cookies interface and load session data, returns same value as load, note | |||
/// if any data was loaded from cookies it would be discarded | |||
/// | |||
/// It can be used for use of an alternative session state medium | |||
/// | |||
bool set_cookie_adapter_and_reload(session_interface_cookie_adapter &adapter); | |||
/// | |||
/// Save the session data, generally should not be called as it is saved automatically. However when | |||
/// writing asynchronous application and using custom slow storage devices like SQL it may be useful to control | |||
/// when and how save() is called. | |||
@@ -335,11 +388,22 @@ public: | |||
/// | |||
std::string get_csrf_token_cookie_name(); | |||
/// | |||
/// Get the session cookie name | |||
/// | |||
std::string session_cookie_name(); | |||
/// | |||
/// Retrun a set of keys that are defined for a current session; | |||
/// | |||
std::set<std::string> key_set(); | |||
private: | |||
friend class http::response; | |||
friend class http::request; | |||
void init(); | |||
impl::cached_settings const &cached_settings(); | |||
struct entry; | |||
@@ -17,11 +17,14 @@ | |||
namespace cppcms { | |||
class service; | |||
namespace impl { | |||
struct cached_settings; | |||
} | |||
namespace sessions { | |||
class encryptor_factory; | |||
class session_storage_factory; | |||
}; | |||
} | |||
namespace json { class value; } | |||
/// | |||
/// \brief This class provides an access to session management backends an allow customization. | |||
@@ -31,14 +34,31 @@ namespace cppcms { | |||
/// | |||
class CPPCMS_API session_pool: public booster::noncopyable { | |||
public: | |||
/// \cond INTERNAL | |||
/// | |||
/// Constructor that is used together with CppCMS service | |||
/// | |||
session_pool(service &srv); | |||
/// | |||
/// Constructor that is used to create independent pool to access the session storage by external tools | |||
/// | |||
session_pool(json::value const &v); | |||
/// | |||
/// Destructor | |||
/// | |||
~session_pool(); | |||
/// | |||
/// Initialize the pool - must be called before get() can be used | |||
/// | |||
/// Note: it allows to install custom session_api, encryptor or storage functionality | |||
/// | |||
void init(); | |||
/// | |||
/// Get an actual object that is used to store/retreive session data | |||
/// | |||
booster::shared_ptr<session_api> get(); | |||
/// \endcond | |||
/// | |||
/// Assign your own implementation of session_api passing pointer to session_api_factory. | |||
@@ -56,6 +76,8 @@ namespace cppcms { | |||
void storage(std::auto_ptr<sessions::session_storage_factory> s); | |||
private: | |||
impl::cached_settings const &cached_settings(); | |||
void after_fork(); | |||
struct cookies_factory; | |||
@@ -72,6 +94,7 @@ namespace cppcms { | |||
friend struct cookies_factory; | |||
friend struct dual_factory; | |||
friend struct sid_factory; | |||
friend class session_interface; | |||
friend class gc_job; | |||
booster::hold_ptr<_data> d; | |||
@@ -127,6 +127,7 @@ namespace impl { | |||
struct cached_session { | |||
int timeout; | |||
std::string expire; | |||
bool disable_automatic_load; | |||
struct cached_cookies { | |||
std::string prefix; | |||
std::string domain; | |||
@@ -140,6 +141,7 @@ namespace impl { | |||
{ | |||
timeout = v.get("session.timeout",24*3600); | |||
expire = v.get("session.expire","browser"); | |||
disable_automatic_load = v.get("session.disable_automatic_load",false); | |||
cookies.prefix = v.get("session.cookies.prefix","cppcms_session"); | |||
cookies.domain = v.get("session.cookies.domain",""); | |||
cookies.path = v.get("session.cookies.path","/"); | |||
@@ -127,7 +127,7 @@ void context::complete_response() | |||
void context::dispatch(booster::intrusive_ptr<application> app,std::string url,bool syncronous) | |||
{ | |||
try { | |||
if(syncronous) | |||
if(syncronous && !app->context().service().cached_settings().session.disable_automatic_load) | |||
app->context().session().load(); | |||
app->main(url); | |||
} | |||
@@ -28,11 +28,13 @@ | |||
#include <sstream> | |||
#include "string.h" | |||
using namespace std; | |||
namespace cppcms { | |||
struct session_interface::_data {}; | |||
struct session_interface::_data { | |||
session_pool *pool; | |||
session_interface_cookie_adapter *adapter; | |||
_data() : pool(0), adapter(0) {} | |||
}; | |||
struct session_interface::entry { | |||
std::string value; | |||
@@ -50,18 +52,12 @@ namespace cppcms { | |||
} | |||
}; | |||
session_interface::session_interface(http::context &context) : | |||
context_(&context), | |||
loaded_(0), | |||
reset_(0), | |||
csrf_checked_(0), | |||
csrf_do_validation_(0), | |||
csrf_validation_(0) | |||
void session_interface::init() | |||
{ | |||
csrf_validation_ = context.service().cached_settings().security.csrf.enable; | |||
csrf_do_validation_ = context.service().cached_settings().security.csrf.automatic; | |||
timeout_val_def_=context.service().cached_settings().session.timeout; | |||
string s_how=context.service().cached_settings().session.expire; | |||
csrf_validation_ = cached_settings().security.csrf.enable; | |||
csrf_do_validation_ = cached_settings().security.csrf.automatic; | |||
timeout_val_def_=cached_settings().session.timeout; | |||
std::string s_how=cached_settings().session.expire; | |||
if(s_how=="fixed") { | |||
how_def_=fixed; | |||
} | |||
@@ -74,7 +70,33 @@ session_interface::session_interface(http::context &context) : | |||
else { | |||
throw cppcms_error("Unsupported `session.expire' type `"+s_how+"'"); | |||
} | |||
} | |||
session_interface::session_interface(session_pool &pool,session_interface_cookie_adapter &adapter) : | |||
context_(0), | |||
loaded_(0), | |||
reset_(0), | |||
csrf_checked_(0), | |||
csrf_do_validation_(0), | |||
csrf_validation_(0), | |||
d(new session_interface::_data()) | |||
{ | |||
d->pool = &pool; | |||
d->adapter = &adapter; | |||
init(); | |||
storage_=d->pool->get(); | |||
} | |||
session_interface::session_interface(http::context &context) : | |||
context_(&context), | |||
loaded_(0), | |||
reset_(0), | |||
csrf_checked_(0), | |||
csrf_do_validation_(0), | |||
csrf_validation_(0), | |||
d(new session_interface::_data()) | |||
{ | |||
init(); | |||
storage_=context_->service().session_pool().get(); | |||
} | |||
@@ -95,6 +117,8 @@ bool session_interface::validate_csrf_token(std::string const &token) | |||
void session_interface::validate_request_origin() | |||
{ | |||
if(!context_) | |||
throw cppcms_error("request origin validation isn't possible without http::context"); | |||
if(csrf_checked_) | |||
return; | |||
csrf_checked_ = 1; | |||
@@ -136,7 +160,7 @@ bool session_interface::load() | |||
data_copy_.clear(); | |||
timeout_val_=timeout_val_def_; | |||
how_=how_def_; | |||
string ar; | |||
std::string ar; | |||
saved_=0; | |||
on_server_=0; | |||
if(!storage_->load(*this,ar,timeout_in_)) { | |||
@@ -152,6 +176,12 @@ bool session_interface::load() | |||
on_server_=get<int>("_s"); | |||
return true; | |||
} | |||
bool session_interface::set_cookie_adapter_and_reload(session_interface_cookie_adapter &adapter) | |||
{ | |||
d->adapter = &adapter; | |||
loaded_ = 0; | |||
return load(); | |||
} | |||
int session_interface::cookie_age() | |||
{ | |||
@@ -219,9 +249,9 @@ void session_interface::load_data(data_type &data,std::string const &s) | |||
packed p(begin,end); | |||
begin +=sizeof(p); | |||
if(end - begin >= int(p.key_size + p.data_size)) { | |||
string key(begin,begin+p.key_size); | |||
std::string key(begin,begin+p.key_size); | |||
begin+=p.key_size; | |||
string val(begin,begin+p.data_size); | |||
std::string val(begin,begin+p.data_size); | |||
begin+=p.data_size; | |||
entry &ent=data[key]; | |||
ent.exposed = p.exposed; | |||
@@ -281,10 +311,10 @@ void session_interface::save() | |||
} | |||
if(new_session_ && context_->service().cached_settings().security.csrf.enable) | |||
if(new_session_ && cached_settings().security.csrf.enable) | |||
{ | |||
set("_csrf",generate_csrf_token()); | |||
if(context_->service().cached_settings().security.csrf.exposed) | |||
if(cached_settings().security.csrf.exposed) | |||
expose("_csrf"); | |||
} | |||
@@ -305,7 +335,7 @@ void session_interface::save() | |||
force_update=true; | |||
} | |||
string ar; | |||
std::string ar; | |||
save_data(data_,ar); | |||
temp_cookie_.clear(); | |||
@@ -323,19 +353,19 @@ void session_interface::check() | |||
throw cppcms_error("Session storage backend is not loaded\n"); | |||
} | |||
string &session_interface::operator[](string const &key) | |||
std::string &session_interface::operator[](std::string const &key) | |||
{ | |||
check(); | |||
return data_[key].value; | |||
} | |||
void session_interface::erase(string const &key) | |||
void session_interface::erase(std::string const &key) | |||
{ | |||
check(); | |||
data_.erase(key); | |||
} | |||
bool session_interface::is_set(string const &key) | |||
bool session_interface::is_set(std::string const &key) | |||
{ | |||
check(); | |||
return data_.find(key)!=data_.end(); | |||
@@ -347,6 +377,18 @@ void session_interface::clear() | |||
data_.clear(); | |||
} | |||
std::set<std::string> session_interface::key_set() | |||
{ | |||
check(); | |||
std::set<std::string> r; | |||
for(data_type::const_iterator p=data_.begin();p!=data_.end();++p) { | |||
if(p->first.c_str()[0]=='_') | |||
continue; | |||
r.insert(p->first); | |||
} | |||
return r; | |||
} | |||
std::string session_interface::get(std::string const &key,std::string const &def) | |||
{ | |||
check(); | |||
@@ -383,22 +425,22 @@ bool session_interface::is_blocking() | |||
return storage_ && storage_->is_blocking(); | |||
} | |||
void session_interface::set_session_cookie(int64_t age,string const &data,string const &key) | |||
void session_interface::set_session_cookie(int64_t age,std::string const &data,std::string const &key) | |||
{ | |||
if(data.empty()) | |||
age=-1; | |||
std::string cookie_name=context_->service().cached_settings().session.cookies.prefix; | |||
std::string cookie_name=cached_settings().session.cookies.prefix; | |||
if(!key.empty()) { | |||
cookie_name+="_"; | |||
cookie_name+=key; | |||
} | |||
std::string const &domain = context_->service().cached_settings().session.cookies.domain; | |||
std::string const &path = context_->service().cached_settings().session.cookies.path; | |||
int time_shift = context_->service().cached_settings().session.cookies.time_shift; | |||
bool use_age = context_->service().cached_settings().session.cookies.use_age; | |||
bool use_exp = context_->service().cached_settings().session.cookies.use_exp; | |||
std::string const &domain = cached_settings().session.cookies.domain; | |||
std::string const &path = cached_settings().session.cookies.path; | |||
int time_shift = cached_settings().session.cookies.time_shift; | |||
bool use_age = cached_settings().session.cookies.use_age; | |||
bool use_exp = cached_settings().session.cookies.use_exp; | |||
bool secure = context_->service().cached_settings().session.cookies.secure; | |||
bool secure = cached_settings().session.cookies.secure; | |||
http::cookie the_cookie(cookie_name,util::urlencode(data),path,domain); | |||
@@ -421,25 +463,38 @@ void session_interface::set_session_cookie(int64_t age,string const &data,string | |||
the_cookie.secure(secure); | |||
context_->response().set_cookie(the_cookie); | |||
if(d->adapter) | |||
d->adapter->set_cookie(the_cookie); | |||
else | |||
context_->response().set_cookie(the_cookie); | |||
} | |||
void session_interface::set_session_cookie(string const &data) | |||
void session_interface::set_session_cookie(std::string const &data) | |||
{ | |||
check(); | |||
temp_cookie_=data; | |||
} | |||
string session_interface::get_session_cookie() | |||
std::string session_interface::session_cookie_name() | |||
{ | |||
return cached_settings().session.cookies.prefix; | |||
} | |||
std::string session_interface::get_session_cookie() | |||
{ | |||
check(); | |||
string name=context_->service().cached_settings().session.cookies.prefix; | |||
http::request::cookies_type const &cookies = context_->request().cookies(); | |||
http::request::cookies_type::const_iterator p=cookies.find(name); | |||
if(p==cookies.end()) | |||
return ""; | |||
return p->second.value(); | |||
std::string const &name=cached_settings().session.cookies.prefix; | |||
if(d->adapter) { | |||
return d->adapter->get_session_cookie(name); | |||
} | |||
else { | |||
http::request::cookies_type const &cookies = context_->request().cookies(); | |||
http::request::cookies_type::const_iterator p=cookies.find(name); | |||
if(p==cookies.end()) | |||
return std::string(); | |||
return p->second.value(); | |||
} | |||
} | |||
bool session_interface::is_exposed(std::string const &key) | |||
@@ -450,12 +505,12 @@ bool session_interface::is_exposed(std::string const &key) | |||
return false; | |||
} | |||
void session_interface::expose(string const &key,bool exp) | |||
void session_interface::expose(std::string const &key,bool exp) | |||
{ | |||
data_[key].exposed=exp; | |||
} | |||
void session_interface::hide(string const &key) | |||
void session_interface::hide(std::string const &key) | |||
{ | |||
check(); | |||
expose(key,false); | |||
@@ -525,7 +580,19 @@ std::string session_interface::get_csrf_token() | |||
std::string session_interface::get_csrf_token_cookie_name() | |||
{ | |||
return context_->service().cached_settings().session.cookies.prefix + "__csrf"; // one for suffix and one for _csrf | |||
return cached_settings().session.cookies.prefix + "__csrf"; // one for suffix and one for _csrf | |||
} | |||
impl::cached_settings const &session_interface::cached_settings() | |||
{ | |||
if(context_) | |||
return context_->service().cached_settings(); | |||
else | |||
return d->pool->cached_settings(); | |||
} | |||
session_interface_cookie_adapter::~session_interface_cookie_adapter() | |||
{ | |||
} | |||
@@ -39,11 +39,14 @@ namespace boost = cppcms_boost; | |||
#include <booster/callback.h> | |||
#include <booster/posix_time.h> | |||
#include "cached_settings.h" | |||
namespace cppcms { | |||
struct session_pool::_data | |||
{ | |||
booster::shared_object module; | |||
cppcms::json::value settings; | |||
booster::hold_ptr<impl::cached_settings> cached_settings; | |||
}; | |||
using namespace cppcms::sessions; | |||
@@ -149,17 +152,18 @@ private: | |||
void session_pool::init() | |||
{ | |||
service &srv=*service_; | |||
cppcms::json::value const &settings = (service_ ? service_->settings() : d->settings); | |||
if(backend_.get()) | |||
return; | |||
std::string location=srv.settings().get("session.location","none"); | |||
std::string location=settings.get("session.location","none"); | |||
if((location == "client" || location=="both") && !encryptor_.get()) { | |||
using namespace cppcms::sessions::impl; | |||
std::string enc=srv.settings().get("session.client.encryptor",""); | |||
std::string mac=srv.settings().get("session.client.hmac",""); | |||
std::string cbc=srv.settings().get("session.client.cbc",""); | |||
std::string enc=settings.get("session.client.encryptor",""); | |||
std::string mac=settings.get("session.client.hmac",""); | |||
std::string cbc=settings.get("session.client.cbc",""); | |||
if(enc.empty() && mac.empty() && cbc.empty()) { | |||
throw cppcms_error("Using clinet session storage without encryption method"); | |||
@@ -175,10 +179,10 @@ void session_pool::init() | |||
if(!enc.empty()) { | |||
crypto::key k; | |||
std::string key_file = srv.settings().get("session.client.key_file",""); | |||
std::string key_file = settings.get("session.client.key_file",""); | |||
if(key_file.empty()) | |||
k=crypto::key(srv.settings().get<std::string>("session.client.key")); | |||
k=crypto::key(settings.get<std::string>("session.client.key")); | |||
else | |||
k.read_from_file(key_file); | |||
@@ -197,9 +201,9 @@ void session_pool::init() | |||
} | |||
else { | |||
crypto::key hmac_key; | |||
std::string hmac_key_file = srv.settings().get("session.client.hmac_key_file",""); | |||
std::string hmac_key_file = settings.get("session.client.hmac_key_file",""); | |||
if(hmac_key_file.empty()) | |||
hmac_key = crypto::key(srv.settings().get<std::string>("session.client.hmac_key")); | |||
hmac_key = crypto::key(settings.get<std::string>("session.client.hmac_key")); | |||
else | |||
hmac_key.read_from_file(hmac_key_file); | |||
@@ -208,10 +212,10 @@ void session_pool::init() | |||
} | |||
else { | |||
crypto::key cbc_key; | |||
std::string cbc_key_file = srv.settings().get("session.client.cbc_key_file",""); | |||
std::string cbc_key_file = settings.get("session.client.cbc_key_file",""); | |||
if(cbc_key_file.empty()) | |||
cbc_key = crypto::key(srv.settings().get<std::string>("session.client.cbc_key")); | |||
cbc_key = crypto::key(settings.get<std::string>("session.client.cbc_key")); | |||
else | |||
cbc_key.read_from_file(cbc_key_file); | |||
@@ -222,29 +226,41 @@ void session_pool::init() | |||
encryptor(factory); | |||
} | |||
if((location == "server" || location == "both") && !storage_.get()) { | |||
std::string stor=srv.settings().get<std::string>("session.server.storage"); | |||
std::string stor=settings.get<std::string>("session.server.storage"); | |||
std::auto_ptr<sessions::session_storage_factory> factory; | |||
if(stor == "files") { | |||
std::string dir = srv.settings().get("session.server.dir",""); | |||
std::string dir = settings.get("session.server.dir",""); | |||
#ifdef CPPCMS_WIN_NATIVE | |||
factory.reset(new session_file_storage_factory(dir)); | |||
#else | |||
bool sharing = srv.settings().get("session.server.shared",true); | |||
int threads = srv.threads_no(); | |||
int procs = srv.procs_no(); | |||
if(procs == 0) procs=1; | |||
factory.reset(new session_file_storage_factory(dir,threads*procs,procs,sharing)); | |||
if(!service_) { | |||
if(!settings.get("session.server.shared",true)) { | |||
throw cppcms_error("When using external session management with file storage " | |||
"session.server.shared must be true"); | |||
} | |||
factory.reset(new session_file_storage_factory(dir,booster::thread::hardware_concurrency()+1,2,true)); | |||
} | |||
else { | |||
bool sharing = settings.get("session.server.shared",true); | |||
int threads = service_->threads_no(); | |||
int procs = service_->procs_no(); | |||
if(procs == 0) procs=1; | |||
factory.reset(new session_file_storage_factory(dir,threads*procs,procs,sharing)); | |||
} | |||
#endif | |||
} | |||
else if(stor == "memory") { | |||
if(srv.procs_no() > 1) | |||
if(!service_) { | |||
throw cppcms_error("Can't use memory storage for external session management"); | |||
} | |||
if(service_->procs_no() > 1) | |||
throw cppcms_error("Can't use memory storage with more then 1 worker process"); | |||
factory.reset(new session_memory_storage_factory()); | |||
} | |||
else if(stor == "external") { | |||
std::string so = srv.settings().get<std::string>("session.server.shared_object",""); | |||
std::string module = srv.settings().get<std::string>("session.server.module",""); | |||
std::string entry_point = srv.settings().get<std::string>("session.server.entry_point","sessions_generator"); | |||
std::string so = settings.get<std::string>("session.server.shared_object",""); | |||
std::string module = settings.get<std::string>("session.server.module",""); | |||
std::string entry_point = settings.get<std::string>("session.server.entry_point","sessions_generator"); | |||
if(so.empty() && module.empty()) | |||
throw cppcms_error( "sessions_pool: session.storage=external " | |||
"and neither session.server.shared_object " | |||
@@ -262,14 +278,14 @@ void session_pool::init() | |||
} | |||
cppcms_session_storage_generator_type f=0; | |||
d->module.symbol(f,entry_point); | |||
factory.reset(f(srv.settings().find("session.server.settings"))); | |||
factory.reset(f(settings.find("session.server.settings"))); | |||
} | |||
#ifndef CPPCMS_NO_TCP_CACHE | |||
else if(stor == "network") { | |||
typedef std::vector<std::string> ips_type; | |||
typedef std::vector<int> ports_type; | |||
ips_type ips = srv.settings().get<ips_type>("session.server.ips"); | |||
ports_type ports = srv.settings().get<ports_type>("session.server.ports"); | |||
ips_type ips = settings.get<ips_type>("session.server.ips"); | |||
ports_type ports = settings.get<ports_type>("session.server.ports"); | |||
if(ips.size() != ports.size()) | |||
throw cppcms_error( "sessions_pool: session.server.ips and " | |||
"session.server.ports are not of the same size"); | |||
@@ -292,7 +308,7 @@ void session_pool::init() | |||
backend(f); | |||
} | |||
else if(location == "both") { | |||
unsigned limit=srv.settings().get("session.client_size_limit",2048); | |||
unsigned limit=settings.get("session.client_size_limit",2048); | |||
std::auto_ptr<session_api_factory> f(new dual_factory(limit,this)); | |||
backend(f); | |||
} | |||
@@ -301,7 +317,8 @@ void session_pool::init() | |||
else | |||
throw cppcms_error("Unknown location"); | |||
service_->after_fork(boost::bind(&session_pool::after_fork,this)); | |||
if(service_) | |||
service_->after_fork(boost::bind(&session_pool::after_fork,this)); | |||
} | |||
session_pool::session_pool(service &srv) : | |||
@@ -310,6 +327,13 @@ session_pool::session_pool(service &srv) : | |||
{ | |||
} | |||
session_pool::session_pool(cppcms::json::value const &v) : | |||
d(new _data()), | |||
service_(0) | |||
{ | |||
d->settings = v; | |||
d->cached_settings.reset(new cppcms::impl::cached_settings(v)); | |||
} | |||
void session_pool::after_fork() | |||
{ | |||
if(backend_.get() && backend_->requires_gc()) { | |||
@@ -348,4 +372,9 @@ void session_pool::storage(std::auto_ptr<sessions::session_storage_factory> s) | |||
storage_=s; | |||
} | |||
cppcms::impl::cached_settings const &session_pool::cached_settings() | |||
{ | |||
return service_? service_->cached_settings() : *d->cached_settings; | |||
} | |||
} /// cppcms |
@@ -11,6 +11,7 @@ | |||
#include <cppcms/http_request.h> | |||
#include <cppcms/http_response.h> | |||
#include <cppcms/http_context.h> | |||
#include <cppcms/http_cookie.h> | |||
#include <cppcms/session_interface.h> | |||
#include <cppcms/serialization.h> | |||
#include <cppcms/json.h> | |||
@@ -27,6 +28,37 @@ struct mydata : public cppcms::serializable { | |||
} | |||
}; | |||
void test_set(cppcms::session_interface &interface,std::string const &values) | |||
{ | |||
std::set<std::string> keys = interface.key_set(); | |||
std::string res; | |||
for(std::set<std::string>::const_iterator p=keys.begin();p!=keys.end();++p) { | |||
if(res.empty()) | |||
res = *p; | |||
else | |||
res += "|" + *p; | |||
} | |||
if(res!=values) | |||
throw std::runtime_error("Key set is invalid:(" + res +")!=(" + values +")"); | |||
} | |||
class adapter : public cppcms::session_interface_cookie_adapter { | |||
public: | |||
void set_cookie(cppcms::http::cookie const &updated_cookie) | |||
{ | |||
if(updated_cookie.name() == ctx_->session().session_cookie_name()) { | |||
ctx_->response().out() << updated_cookie.value(); | |||
} | |||
} | |||
std::string get_session_cookie(std::string const &/*name*/) | |||
{ | |||
return ctx_->request().get("sid"); | |||
} | |||
private: | |||
cppcms::http::context *ctx_; | |||
}; | |||
class unit_test : public cppcms::application { | |||
public: | |||
unit_test(cppcms::service &s) : cppcms::application(s) | |||
@@ -104,7 +136,9 @@ public: | |||
else if(u=="/api") { | |||
try { | |||
// Fix Me Later | |||
test_set(session(),""); | |||
session().set("x","10"); | |||
test_set(session(),"x"); | |||
TEST(session().get<std::string>("x")=="10"); | |||
TEST(session().get<int>("x")==10); | |||
TEST(session().get<double>("x")==10.0); | |||
@@ -112,6 +146,7 @@ public: | |||
TEST(session().get("x","default")=="10"); | |||
TEST(session()["x"]=="10"); | |||
TEST(session()["z"]==""); | |||
test_set(session(),"x|z"); | |||
session()["z"]="test"; | |||
TEST(session()["z"]=="test"); | |||
@@ -120,6 +155,7 @@ public: | |||
a.y=20; | |||
session().store_data("tmp",a); | |||
session().fetch_data("tmp",b); | |||
test_set(session(),"tmp|x|z"); | |||
TEST(b.x==10); | |||
TEST(b.y==20); | |||