summaryrefslogtreecommitdiff
path: root/project2/json/couchSession.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'project2/json/couchSession.cpp')
-rw-r--r--project2/json/couchSession.cpp211
1 files changed, 211 insertions, 0 deletions
diff --git a/project2/json/couchSession.cpp b/project2/json/couchSession.cpp
new file mode 100644
index 0000000..d2eeca2
--- /dev/null
+++ b/project2/json/couchSession.cpp
@@ -0,0 +1,211 @@
+#include <pch.hpp>
+#include "curlHelper.h"
+#include "safeMapFind.h"
+#include "exceptions.h"
+#include "../libmisc/buffer.h"
+#include "../libmisc/curlsup.h"
+#include <xmlObjectLoader.h>
+#include <sessionContainer.h>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+#include "json.h"
+#include "safeMapFind.h"
+#include "conversion.h"
+#include <boost/program_options.hpp>
+
+class CouchSession : public Session {
+ public:
+ CouchSession(const UUID & sid) : Session(sid) {
+ }
+ CouchSession(const UUID & sid, const json::Object & o) : Session(sid), obj(o) {
+ }
+ virtual VariableType GetValue(const Glib::ustring & name) const {
+ return boost::apply_visitor(JsonToProject2(), *safeMapFind<VariableNotFound>(obj, name)->second);
+ }
+ virtual Values GetValuesCopy() const {
+ Values vs;
+ BOOST_FOREACH(const json::Object::value_type & v, obj) {
+ vs.insert(Values::value_type(v.first, boost::apply_visitor(JsonToProject2(), *v.second)));
+ }
+ return vs;
+ }
+ virtual void SetValue(const Glib::ustring & name, const VariableType & v) {
+ obj[name] = json::ValuePtr(new json::Value(boost::apply_visitor(Project2ToJson(), v)));
+ }
+ virtual void ClearValue(const Glib::ustring & name) {
+ obj.erase(name);
+ }
+ virtual time_t ExpiryTime() const {
+ return boost::get<json::Number>(*safeMapFind<VariableNotFound>(obj, ExpiryKey)->second);
+ }
+ virtual void ExpiryTime(time_t t) {
+ obj[ExpiryKey] = json::ValuePtr(new json::Value(json::Number(t)));
+ }
+ private:
+ json::Object obj;
+ friend class CouchSessionContainer;
+ friend class CustomCouchSessionLoader;
+ static const Glib::ustring ExpiryKey;
+};
+const Glib::ustring CouchSession::ExpiryKey("project2:expires");
+
+class CouchDBFailure : public std::exception { };
+
+class CouchSessionContainer : public SessionContainer {
+ public:
+ CouchSessionContainer() {
+ }
+ virtual SessionPtr getSession(const UUID & uuid) const {
+ try {
+ SessionPtr s = new CouchSession(uuid, getSessionFromServer(uuid));
+ if (s->ExpiryTime() > time(NULL)) {
+ return s;
+ }
+ }
+ catch (...) {
+ }
+ return new CouchSession(UUID::generate_random());
+ }
+
+ virtual void SaveSession(SessionPtr s) const {
+ CurlPtr c = new Curl();
+ c->setopt(CURLOPT_UPLOAD, 1L);
+ c->setopt(CURLOPT_FAILONERROR, 1);
+ Glib::ustring out = json::serializeObject(boost::dynamic_pointer_cast<CouchSession>(s)->obj);
+ c->setopt(CURLOPT_INFILESIZE_LARGE, (curl_off_t)out.size());
+ unsigned int off = 0;
+ BOOST_FOREACH(const std::string & b, baseUrls) {
+ c->setopt(CURLOPT_URL, (b + s->ID.str()).c_str());
+ try {
+ c->performSend(boost::bind(send, &out, &off, _1, _2));
+ return;
+ }
+ catch (...) {
+ }
+ }
+ throw CouchDBFailure();
+ }
+
+ json::Object getSessionFromServer(const UUID & uuid) const {
+ CurlPtr c = new Curl();
+ c->setopt(CURLOPT_FAILONERROR, 1);
+ Glib::ustring msg;
+ BOOST_FOREACH(const std::string & b, baseUrls) {
+ try {
+ c->setopt(CURLOPT_URL, (b + uuid.str()).c_str());
+ c->performRead(boost::bind(append, &msg, _1, _2));
+ json::Object o = json::parseObject(msg);
+ return o;
+ }
+ catch (...) {
+ }
+ }
+ throw CouchDBFailure();
+ }
+
+ static size_t send(Glib::ustring * buf, unsigned int * off, char * str, size_t l) {
+ size_t len = std::min(buf->size() - *off, l);
+ memcpy(str, buf->c_str() + *off, len);
+ return len;
+ }
+
+ static size_t append(Glib::ustring * buf, const char * str, size_t l) {
+ buf->append(str, l);
+ return l;
+ }
+
+ void setopt_s(CurlHandle::Ptr c, CURLoption o, const char * v) {
+ c->setopt(o, v);
+ }
+
+ void setopt_l(CurlHandle::Ptr c, CURLoption o, int64_t v) {
+ c->setopt(o, (long)v);
+ }
+
+ static std::vector<std::string> baseUrls;
+};
+std::vector<std::string> CouchSessionContainer::baseUrls;
+
+namespace po = boost::program_options;
+class CustomCouchSessionLoader : public SessionContainerLoaderImpl<CouchSessionContainer> {
+ public:
+ CustomCouchSessionLoader() :
+ opts("Session CouchDB options")
+ {
+ opts.add_options()
+ ("session.couchdb.baseurl", po::value(&CouchSessionContainer::baseUrls)->composing(),
+ "Base URL to store sessions in")
+ ;
+ }
+ po::options_description *
+ options()
+ {
+ return &opts;
+ }
+
+ void onPeriodic() {
+ deleteSessions();
+ compactDB();
+ }
+
+ private:
+ static size_t discard(size_t l) {
+ return l;
+ }
+
+ void compactDB() {
+ CurlPtr c = new Curl();
+ c->setopt(CURLOPT_POST, 1);
+ c->appendHeader("Content-Type: application/json");
+ BOOST_FOREACH(const std::string & b, CouchSessionContainer::baseUrls) {
+ c->setopt(CURLOPT_URL, (b + "_compact").c_str());
+ c->performRead(boost::bind(discard, _2));
+ }
+ }
+ void deleteSessions() {
+ // Create the server side search map
+ json::Object map;
+ Buffer mapBuf;
+ mapBuf.appendf("function(doc) { var exp = doc['%s']; if (exp < (new Date().getTime() / 1000)) { emit(exp, doc._rev); } }",
+ CouchSession::ExpiryKey.c_str());
+ map["map"] = json::ValuePtr(new json::Value(mapBuf.str()));
+ Glib::ustring mapStr(json::serializeObject(map));
+ // Create the CURL handle
+ CurlPtr c = new Curl();
+ c->setopt(CURLOPT_FAILONERROR, 1);
+ c->appendHeader("Content-Type: application/json");
+ c->setopt(CURLOPT_POST, 1);
+ c->setopt(CURLOPT_POSTFIELDS, mapStr.c_str());
+ c->setopt(CURLOPT_POSTFIELDSIZE, mapStr.bytes());
+ BOOST_FOREACH(const std::string & b, CouchSessionContainer::baseUrls) {
+ Glib::ustring msg;
+ try {
+ c->setopt(CURLOPT_URL, (b + "_temp_view").c_str());
+ c->performRead(boost::bind(CouchSessionContainer::append, &msg, _1, _2));
+ json::Object o = json::parseObject(msg);
+ BOOST_FOREACH(const json::Array::value_type & v, boost::get<json::Array>(*safeMapFind<ParamNotFound>(o, "rows")->second)) {
+ json::Object rec = boost::get<json::Object>(*v);
+ UUID u = boost::get<json::String>(*safeMapFind<ParamNotFound>(rec, "id")->second).raw();
+ Glib::ustring & rev = boost::get<json::String>(*safeMapFind<ParamNotFound>(rec, "value")->second);
+ deleteSession(u, rev);
+ }
+ return;
+ }
+ catch (...) {
+ }
+ }
+ }
+
+ void deleteSession(const UUID & sid, const Glib::ustring & rev) const {
+ CurlPtr c = new Curl();
+ c->setopt(CURLOPT_CUSTOMREQUEST, "DELETE");
+ BOOST_FOREACH(const std::string & b, CouchSessionContainer::baseUrls) {
+ c->setopt(CURLOPT_URL, (b + sid.str() + "?rev=" + rev).c_str());
+ c->performRead(boost::bind(discard, _2));
+ return;
+ }
+ }
+ po::options_description opts;
+};
+DECLARE_CUSTOM_COMPONENT_LOADER("couchsession", CouchSessionContainer, CustomCouchSessionLoader, SessionContainerLoader);
+