summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2016-10-08 21:39:17 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2016-10-08 21:39:17 +0100
commitc523f40ddf83eb7bd49ab50c913ac65fd31f0139 (patch)
treef8d80f852c8bbcb26d405c3531362cf987bfc53c
parentAdd ability to load plugins (such as a session manager service) into the core... (diff)
downloadicespider-c523f40ddf83eb7bd49ab50c913ac65fd31f0139.tar.bz2
icespider-c523f40ddf83eb7bd49ab50c913ac65fd31f0139.tar.xz
icespider-c523f40ddf83eb7bd49ab50c913ac65fd31f0139.zip
Adds a session manager interface and basic file based implementation as a plugin
-rw-r--r--icespider/common/session.ice27
-rw-r--r--icespider/fileSessions/Jamfile.jam13
-rw-r--r--icespider/fileSessions/fileSessions.cpp132
-rw-r--r--icespider/unittests/Jamfile.jam14
-rw-r--r--icespider/unittests/testFileSessions.cpp112
5 files changed, 298 insertions, 0 deletions
diff --git a/icespider/common/session.ice b/icespider/common/session.ice
new file mode 100644
index 0000000..4947f9f
--- /dev/null
+++ b/icespider/common/session.ice
@@ -0,0 +1,27 @@
+#ifndef ICESPIDER_SESSION_ICE
+#define ICESPIDER_SESSION_ICE
+
+module IceSpider {
+ dictionary<string, string> Variables;
+
+ exception SessionError {
+ string what;
+ };
+
+ class Session {
+ string id;
+ long lastUsed;
+ short duration;
+ Variables variables;
+ };
+
+ interface SessionManager {
+ Session createSession() throws SessionError;
+ Session getSession(string id) throws SessionError;
+ void updateSession(Session session) throws SessionError;
+ void destroySession(string id) throws SessionError;
+ };
+};
+
+#endif
+
diff --git a/icespider/fileSessions/Jamfile.jam b/icespider/fileSessions/Jamfile.jam
new file mode 100644
index 0000000..f08559d
--- /dev/null
+++ b/icespider/fileSessions/Jamfile.jam
@@ -0,0 +1,13 @@
+lib adhocutil : : : : <include>/usr/include/adhocutil ;
+lib boost_system ;
+lib boost_filesystem ;
+
+lib icespider-filesessions :
+ [ glob *.cpp ]
+ :
+ <library>boost_system
+ <library>boost_filesystem
+ <library>adhocutil
+ <library>../core//icespider-core
+ ;
+
diff --git a/icespider/fileSessions/fileSessions.cpp b/icespider/fileSessions/fileSessions.cpp
new file mode 100644
index 0000000..d2d5823
--- /dev/null
+++ b/icespider/fileSessions/fileSessions.cpp
@@ -0,0 +1,132 @@
+#include <core.h>
+#include <session.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <factory.impl.h>
+#include <boost/filesystem/operations.hpp>
+#include <boost/uuid/uuid_generators.hpp>
+#include <boost/uuid/uuid_io.hpp>
+#include <Ice/Stream.h>
+#include "Ice/Initialize.h"
+
+namespace IceSpider {
+ class FileSessions : public Plugin, public SessionManager {
+ public:
+ FileSessions(Ice::CommunicatorPtr c, Ice::PropertiesPtr p) :
+ ic(c),
+ root(p->getProperty("IceSpider.FileSessions.Path")),
+ duration(p->getPropertyAsIntWithDefault("IceSpider.FileSessions.Duration", 3600))
+ {
+ if (!root.empty())
+ if (!boost::filesystem::exists(root))
+ boost::filesystem::create_directories(root);
+ }
+
+ ~FileSessions()
+ {
+ removeExpired();
+ }
+
+ SessionPtr createSession(const ::Ice::Current &) override
+ {
+ SessionPtr s = new Session();
+ s->id = boost::lexical_cast<std::string>(boost::uuids::random_generator()());
+ s->duration = duration;
+ save(s);
+ return s;
+ }
+
+ SessionPtr getSession(const ::std::string & id, const ::Ice::Current & current) override
+ {
+ auto s = load(id);
+ if (s && isExpired(s)) {
+ destroySession(id, current);
+ return NULL;
+ }
+ return s;
+ }
+
+ void updateSession(const SessionPtr & s, const ::Ice::Current &) override
+ {
+ save(s);
+ }
+
+ void destroySession(const ::std::string & id, const ::Ice::Current &) override
+ {
+ try {
+ boost::filesystem::remove(root / id);
+ }
+ catch (const std::exception & e) {
+ throw SessionError(e.what());
+ }
+ }
+
+ private:
+ void save(SessionPtr s)
+ {
+ time(&s->lastUsed);
+ auto buf = Ice::createOutputStream(ic);
+ buf->write(s);
+ auto range = buf->finished();
+ auto fd = sysassert(open((root / s->id).c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR), -1);
+ sysassert(flock(fd, LOCK_EX), -1);
+ sysassert(pwrite(fd, range.first, range.second - range.first, 0), -1);
+ sysassert(ftruncate(fd, range.second - range.first), -1);
+ sysassert(flock(fd, LOCK_UN), -1);
+ sysassert(close(fd), -1);
+ }
+
+ SessionPtr load(const std::string & id)
+ {
+ int fd = open((root / id).c_str(), O_RDONLY);
+ if (fd == -1 && errno == ENOENT) return NULL;
+ sysassert(fd, -1);
+ sysassert(flock(fd, LOCK_SH), -1);
+ struct stat st;
+ sysassert(fstat(fd, &st), -1);
+ auto fbuf = sysassert((Ice::Byte *)mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0), (Ice::Byte *)NULL);
+ auto buf = Ice::createInputStream(ic, std::make_pair(fbuf, fbuf + st.st_size));
+ SessionPtr s;
+ buf->read(s);
+ sysassert(flock(fd, LOCK_UN), -1);
+ sysassert(close(fd), -1);
+ return s;
+ }
+
+ void removeExpired()
+ {
+ if (root.empty() || !boost::filesystem::exists(root)) return;
+ boost::filesystem::directory_iterator di(root);
+ while (di != boost::filesystem::directory_iterator()) {
+ auto s = load(di->path().leaf().string());
+ if (s && isExpired(s)) {
+ destroySession(s->id, Ice::Current());
+ }
+ di++;
+ }
+ }
+
+ bool isExpired(SessionPtr s)
+ {
+ return (s->lastUsed + s->duration < time(NULL));
+ }
+
+ template<typename R, typename ER>
+ R sysassert(R rtn, ER ertn)
+ {
+ if (rtn == ertn) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ throw SessionError(strerror(errno));
+ }
+ return rtn;
+ }
+
+ Ice::CommunicatorPtr ic;
+ const boost::filesystem::path root;
+ const Ice::Int duration;
+ };
+}
+NAMEDFACTORY("IceSpider-FileSessions", IceSpider::FileSessions, IceSpider::PluginFactory);
+
diff --git a/icespider/unittests/Jamfile.jam b/icespider/unittests/Jamfile.jam
index 4ee2bd8..ba7e9c2 100644
--- a/icespider/unittests/Jamfile.jam
+++ b/icespider/unittests/Jamfile.jam
@@ -100,6 +100,20 @@ run
<include>../fcgi
: testFcgi ;
+run
+ testFileSessions.cpp
+ : :
+ config/ice.properties
+ :
+ <define>BOOST_TEST_DYN_LINK
+ <library>../fileSessions//icespider-filesessions
+ <library>../common//icespider-common
+ <library>testCommon
+ <library>boost_system
+ <library>boost_filesystem
+ <library>../core//icespider-core
+ ;
+
lib test-api :
test-api.ice
:
diff --git a/icespider/unittests/testFileSessions.cpp b/icespider/unittests/testFileSessions.cpp
new file mode 100644
index 0000000..37e6587
--- /dev/null
+++ b/icespider/unittests/testFileSessions.cpp
@@ -0,0 +1,112 @@
+#define BOOST_TEST_MODULE TestFileSessions
+#include <boost/test/unit_test.hpp>
+
+#include <Ice/Initialize.h>
+#include <Ice/Properties.h>
+#include <session.h>
+#include <core.h>
+#include <definedDirs.h>
+
+BOOST_TEST_DONT_PRINT_LOG_VALUE(IceSpider::StringMap);
+
+class TestCore : public IceSpider::Core {
+ public:
+ TestCore() :
+ IceSpider::Core({
+ "--IceSpider.SessionManager=IceSpider-FileSessions",
+ "--IceSpider.FileSessions.Path=" + (binDir / "test-sessions").string(),
+ "--IceSpider.FileSessions.Duration=0"
+ }),
+ root(communicator->getProperties()->getProperty("IceSpider.FileSessions.Path"))
+ {
+ }
+ const boost::filesystem::path root;
+};
+
+BOOST_AUTO_TEST_CASE( clear )
+{
+ TestCore tc;
+ if (boost::filesystem::exists(tc.root)) {
+ boost::filesystem::remove_all(tc.root);
+ }
+}
+
+BOOST_FIXTURE_TEST_SUITE(Core, TestCore);
+
+BOOST_AUTO_TEST_CASE( ping )
+{
+ auto prx = this->getProxy<IceSpider::SessionManager>();
+ BOOST_REQUIRE(prx);
+ prx->ice_ping();
+}
+
+BOOST_AUTO_TEST_CASE( createAndDestroy )
+{
+ auto prx = this->getProxy<IceSpider::SessionManager>();
+ auto s = prx->createSession();
+ BOOST_REQUIRE(boost::filesystem::exists(root / s->id));
+ BOOST_REQUIRE_EQUAL(0, s->duration);
+ BOOST_REQUIRE_EQUAL(time(NULL), s->lastUsed);
+ prx->destroySession(s->id);
+ BOOST_REQUIRE(!boost::filesystem::exists(root / s->id));
+}
+
+BOOST_AUTO_TEST_CASE( createAndChangeRestore )
+{
+ auto prx = this->getProxy<IceSpider::SessionManager>();
+ auto s = prx->createSession();
+ s->variables["a"] = "value";
+ prx->updateSession(s);
+
+ auto s2 = prx->getSession(s->id);
+ BOOST_REQUIRE_EQUAL(s->id, s2->id);
+ BOOST_REQUIRE_EQUAL(s->variables, s2->variables);
+
+ prx->destroySession(s->id);
+}
+
+BOOST_AUTO_TEST_CASE( createAndExpire )
+{
+ auto prx = this->getProxy<IceSpider::SessionManager>();
+ auto s = prx->createSession();
+ BOOST_REQUIRE(boost::filesystem::exists(root / s->id));
+ BOOST_REQUIRE_EQUAL(0, s->duration);
+ BOOST_REQUIRE_EQUAL(time(NULL), s->lastUsed);
+ usleep(1001000);
+ BOOST_REQUIRE(boost::filesystem::exists(root / s->id));
+ BOOST_REQUIRE(!prx->getSession(s->id));
+ BOOST_REQUIRE(!boost::filesystem::exists(root / s->id));
+}
+
+BOOST_AUTO_TEST_CASE( missing )
+{
+ auto prx = this->getProxy<IceSpider::SessionManager>();
+ BOOST_REQUIRE(!prx->getSession("missing"));
+ BOOST_REQUIRE(!boost::filesystem::exists(root / "missing"));
+}
+
+BOOST_AUTO_TEST_CASE( createAndLeave )
+{
+ auto prx = this->getProxy<IceSpider::SessionManager>();
+ auto s = prx->createSession();
+ BOOST_REQUIRE(boost::filesystem::exists(root / s->id));
+}
+
+BOOST_AUTO_TEST_CASE( left )
+{
+ BOOST_REQUIRE(!boost::filesystem::is_empty(root));
+}
+
+BOOST_AUTO_TEST_CASE( expire )
+{
+ usleep(1001000);
+}
+
+BOOST_AUTO_TEST_SUITE_END();
+
+BOOST_AUTO_TEST_CASE( empty )
+{
+ TestCore tc;
+ BOOST_REQUIRE(boost::filesystem::is_empty(tc.root));
+}
+