diff options
Diffstat (limited to 'project2/json/couchSession.cpp')
-rw-r--r-- | project2/json/couchSession.cpp | 211 |
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); + |