ChipMaster's trial hacks on C++CMS starting with v1.2.1. Not sure I'll follow on with the v2 since it looks to be breaking and mostly frivolous.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

386 lines
11 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
  4. //
  5. // See accompanying file COPYING.TXT file for licensing details.
  6. //
  7. ///////////////////////////////////////////////////////////////////////////////
  8. #define CPPCMS_SOURCE
  9. #include <cppcms/session_pool.h>
  10. #include <cppcms/service.h>
  11. #include <cppcms/thread_pool.h>
  12. #include <cppcms/session_cookies.h>
  13. #include <cppcms/session_sid.h>
  14. #include <cppcms/session_dual.h>
  15. #include "hmac_encryptor.h"
  16. #include <cppcms/json.h>
  17. #include <cppcms/cppcms_error.h>
  18. #include <booster/shared_ptr.h>
  19. #include <booster/shared_object.h>
  20. #include <booster/enable_shared_from_this.h>
  21. #include <cppcms/config.h>
  22. #include <cppcms/mem_bind.h>
  23. #include "aes_encryptor.h"
  24. #ifdef CPPCMS_WIN_NATIVE
  25. #include "session_win32_file_storage.h"
  26. #else
  27. #include "session_posix_file_storage.h"
  28. #endif
  29. #include "session_memory_storage.h"
  30. #ifndef CPPCMS_NO_TCP_CACHE
  31. #include "session_tcp_storage.h"
  32. #endif
  33. #include <booster/aio/deadline_timer.h>
  34. #include <booster/callback.h>
  35. #include <booster/posix_time.h>
  36. #include <booster/system_error.h>
  37. #include "cached_settings.h"
  38. namespace cppcms {
  39. struct session_pool::_data
  40. {
  41. booster::shared_object module;
  42. cppcms::json::value settings;
  43. booster::hold_ptr<impl::cached_settings> cached_settings;
  44. };
  45. using namespace cppcms::sessions;
  46. struct session_pool::sid_factory : public session_api_factory
  47. {
  48. sid_factory(session_pool *pool) : pool_(pool) {}
  49. bool requires_gc() {
  50. if(pool_->storage_.get())
  51. return pool_->storage_->requires_gc();
  52. else
  53. return false;
  54. }
  55. void gc()
  56. {
  57. if(pool_->storage_.get())
  58. pool_->storage_->gc_job();
  59. }
  60. booster::shared_ptr<session_api> get()
  61. {
  62. booster::shared_ptr<session_api> p;
  63. if(pool_->storage_.get())
  64. p.reset(new session_sid(pool_->storage_->get()));
  65. return p;
  66. }
  67. private:
  68. session_pool *pool_;
  69. };
  70. struct session_pool::cookies_factory : public session_api_factory
  71. {
  72. cookies_factory(session_pool *pool) : pool_(pool) {}
  73. bool requires_gc() {
  74. return false;
  75. }
  76. void gc() {}
  77. booster::shared_ptr<session_api> get()
  78. {
  79. booster::shared_ptr<session_api> p;
  80. if(pool_->encryptor_.get())
  81. p.reset(new session_cookies(pool_->encryptor_->get()));
  82. return p;
  83. }
  84. private:
  85. session_pool *pool_;
  86. };
  87. struct session_pool::dual_factory : public session_api_factory
  88. {
  89. dual_factory(unsigned limit,session_pool *pool) : limit_(limit), pool_(pool) {}
  90. bool requires_gc() {
  91. if(pool_->storage_.get())
  92. return pool_->storage_->requires_gc();
  93. else
  94. return false;
  95. }
  96. void gc()
  97. {
  98. if(pool_->storage_.get())
  99. pool_->storage_->gc_job();
  100. }
  101. booster::shared_ptr<session_api> get()
  102. {
  103. booster::shared_ptr<session_api> p;
  104. if(pool_->storage_.get() && pool_->encryptor_.get())
  105. p.reset(new session_dual(pool_->encryptor_->get(),pool_->storage_->get(),limit_));
  106. return p;
  107. }
  108. private:
  109. unsigned limit_;
  110. session_pool *pool_;
  111. };
  112. class session_pool::gc_job : public booster::enable_shared_from_this<gc_job> {
  113. public:
  114. gc_job(service *ser,double freq,session_pool *pool) :
  115. timer_(new booster::aio::deadline_timer(ser->get_io_service())),
  116. service_(ser),
  117. freq_(freq),
  118. pool_(pool)
  119. {
  120. }
  121. void async_run(booster::system::error_code const &e)
  122. {
  123. if(e)
  124. return;
  125. service_->thread_pool().post(cppcms::util::mem_bind(&gc_job::gc,shared_from_this()));
  126. }
  127. private:
  128. void gc()
  129. {
  130. booster::ptime start = booster::ptime::now();
  131. booster::ptime restart = start + booster::ptime::from_number(freq_);
  132. pool_->backend_->gc();
  133. timer_->expires_at(restart);
  134. timer_->async_wait(cppcms::util::mem_bind(&gc_job::async_run,shared_from_this()));
  135. }
  136. booster::shared_ptr<booster::aio::deadline_timer> timer_;
  137. service *service_;
  138. double freq_;
  139. session_pool *pool_;
  140. };
  141. void session_pool::init()
  142. {
  143. cppcms::json::value const &settings = (service_ ? service_->settings() : d->settings);
  144. if(backend_.get())
  145. return;
  146. std::string location=settings.get("session.location","none");
  147. if((location == "client" || location=="both") && !encryptor_.get()) {
  148. using namespace cppcms::sessions::impl;
  149. std::string enc=settings.get("session.client.encryptor","");
  150. std::string mac=settings.get("session.client.hmac","");
  151. std::string cbc=settings.get("session.client.cbc","");
  152. if(enc.empty() && mac.empty() && cbc.empty()) {
  153. throw cppcms_error("Using clinet session storage without encryption method");
  154. }
  155. if(!enc.empty() && (!mac.empty() || !cbc.empty())) {
  156. throw cppcms_error("Can't specify both session.client.encryptor and session.client.hmac or session.client.cbc");
  157. }
  158. if(!cbc.empty() && mac.empty()) {
  159. throw cppcms_error("Can't use session encryption without MAC");
  160. }
  161. std::auto_ptr<cppcms::sessions::encryptor_factory> factory;
  162. if(!enc.empty()) {
  163. crypto::key k;
  164. std::string key_file = settings.get("session.client.key_file","");
  165. if(key_file.empty())
  166. k=crypto::key(settings.get<std::string>("session.client.key"));
  167. else
  168. k.read_from_file(key_file);
  169. if(enc=="hmac") {
  170. factory.reset(new hmac_factory("sha1",k));
  171. }
  172. else if(enc.compare(0,5,"hmac-") == 0) {
  173. std::string algo = enc.substr(5);
  174. factory.reset(new hmac_factory(algo,k));
  175. }
  176. else if(enc.compare(0,3,"aes") == 0) {
  177. factory.reset(new aes_factory(enc,k));
  178. }
  179. else
  180. throw cppcms_error("Unknown encryptor: "+enc);
  181. }
  182. else {
  183. crypto::key hmac_key;
  184. std::string hmac_key_file = settings.get("session.client.hmac_key_file","");
  185. if(hmac_key_file.empty())
  186. hmac_key = crypto::key(settings.get<std::string>("session.client.hmac_key"));
  187. else
  188. hmac_key.read_from_file(hmac_key_file);
  189. if(cbc.empty()) {
  190. factory.reset(new hmac_factory(mac,hmac_key));
  191. }
  192. else {
  193. crypto::key cbc_key;
  194. std::string cbc_key_file = settings.get("session.client.cbc_key_file","");
  195. if(cbc_key_file.empty())
  196. cbc_key = crypto::key(settings.get<std::string>("session.client.cbc_key"));
  197. else
  198. cbc_key.read_from_file(cbc_key_file);
  199. factory.reset(new aes_factory(cbc,cbc_key,mac,hmac_key));
  200. }
  201. }
  202. encryptor(factory);
  203. }
  204. if((location == "server" || location == "both") && !storage_.get()) {
  205. std::string stor=settings.get<std::string>("session.server.storage");
  206. std::auto_ptr<sessions::session_storage_factory> factory;
  207. #ifndef CPPCMS_NO_GZIP
  208. if(stor == "files") {
  209. std::string dir = settings.get("session.server.dir","");
  210. #ifdef CPPCMS_WIN_NATIVE
  211. factory.reset(new session_file_storage_factory(dir));
  212. #else
  213. if(!service_) {
  214. if(!settings.get("session.server.shared",true)) {
  215. throw cppcms_error("When using external session management with file storage "
  216. "session.server.shared must be true");
  217. }
  218. factory.reset(new session_file_storage_factory(dir,booster::thread::hardware_concurrency()+1,2,true));
  219. }
  220. else {
  221. bool sharing = settings.get("session.server.shared",true);
  222. int threads = service_->threads_no();
  223. int procs = service_->procs_no();
  224. if(procs == 0) procs=1;
  225. factory.reset(new session_file_storage_factory(dir,threads*procs,procs,sharing));
  226. }
  227. #endif
  228. }
  229. else
  230. #endif// CPPCMS_NO_GZIP
  231. if(stor == "memory") {
  232. if(!service_) {
  233. throw cppcms_error("Can't use memory storage for external session management");
  234. }
  235. if(service_->procs_no() > 1)
  236. throw cppcms_error("Can't use memory storage with more then 1 worker process");
  237. factory.reset(new session_memory_storage_factory());
  238. }
  239. else if(stor == "external") {
  240. std::string so = settings.get<std::string>("session.server.shared_object","");
  241. std::string module = settings.get<std::string>("session.server.module","");
  242. std::string entry_point = settings.get<std::string>("session.server.entry_point","sessions_generator");
  243. if(so.empty() && module.empty())
  244. throw cppcms_error( "sessions_pool: session.storage=external "
  245. "and neither session.server.shared_object "
  246. "nor session.server.module is defined");
  247. if(!so.empty() && !module.empty())
  248. throw cppcms_error( "sessions_pool: both session.server.shared_object "
  249. "and session.server.module are defined");
  250. if(so.empty()) {
  251. so = booster::shared_object::name(module);
  252. }
  253. std::string error;
  254. if(!d->module.open(so,error)) {
  255. throw cppcms_error("sessions_pool: failed to load shared object " + so + ": " + error);
  256. }
  257. cppcms_session_storage_generator_type f=0;
  258. d->module.symbol(f,entry_point);
  259. factory.reset(f(settings.find("session.server.settings")));
  260. }
  261. #ifndef CPPCMS_NO_TCP_CACHE
  262. else if(stor == "network") {
  263. typedef std::vector<std::string> ips_type;
  264. typedef std::vector<int> ports_type;
  265. ips_type ips = settings.get<ips_type>("session.server.ips");
  266. ports_type ports = settings.get<ports_type>("session.server.ports");
  267. if(ips.size() != ports.size())
  268. throw cppcms_error( "sessions_pool: session.server.ips and "
  269. "session.server.ports are not of the same size");
  270. if(ips.empty()) {
  271. throw cppcms_error("sessions_pool:session.server.ips is empty");
  272. }
  273. factory.reset(new tcp_factory(ips,ports));
  274. }
  275. #endif
  276. else
  277. throw cppcms_error("sessions_pool: unknown server side storage:"+stor);
  278. storage(factory);
  279. }
  280. if(location == "server") {
  281. std::auto_ptr<session_api_factory> f(new sid_factory(this));
  282. backend(f);
  283. }
  284. else if(location == "client") {
  285. std::auto_ptr<session_api_factory> f(new cookies_factory(this));
  286. backend(f);
  287. }
  288. else if(location == "both") {
  289. unsigned limit=settings.get("session.client_size_limit",2048);
  290. std::auto_ptr<session_api_factory> f(new dual_factory(limit,this));
  291. backend(f);
  292. }
  293. else if(location == "none")
  294. ;
  295. else
  296. throw cppcms_error("Unknown location");
  297. if(service_)
  298. service_->after_fork(cppcms::util::mem_bind(&session_pool::after_fork,this));
  299. }
  300. session_pool::session_pool(service &srv) :
  301. d(new _data()),
  302. service_(&srv)
  303. {
  304. }
  305. session_pool::session_pool(cppcms::json::value const &v) :
  306. d(new _data()),
  307. service_(0)
  308. {
  309. d->settings = v;
  310. d->cached_settings.reset(new cppcms::impl::cached_settings(v));
  311. }
  312. void session_pool::after_fork()
  313. {
  314. if(backend_.get() && backend_->requires_gc()) {
  315. if(service_->process_id()!=1)
  316. return;
  317. double frequency = service_->settings().get("session.gc",0.0);
  318. if(frequency > 0) {
  319. booster::shared_ptr<gc_job> job(new gc_job(service_,frequency,this));
  320. job->async_run(booster::system::error_code());
  321. }
  322. }
  323. }
  324. session_pool::~session_pool()
  325. {
  326. }
  327. booster::shared_ptr<session_api> session_pool::get()
  328. {
  329. booster::shared_ptr<session_api> p;
  330. if(backend_.get())
  331. p=backend_->get();
  332. return p;
  333. }
  334. void session_pool::backend(std::auto_ptr<session_api_factory> b)
  335. {
  336. backend_=b;
  337. }
  338. void session_pool::encryptor(std::auto_ptr<sessions::encryptor_factory> e)
  339. {
  340. encryptor_=e;
  341. }
  342. void session_pool::storage(std::auto_ptr<sessions::session_storage_factory> s)
  343. {
  344. storage_=s;
  345. }
  346. cppcms::impl::cached_settings const &session_pool::cached_settings()
  347. {
  348. return service_? service_->cached_settings() : *d->cached_settings;
  349. }
  350. } /// cppcms