diff options
-rw-r--r-- | icespider/common/session.ice | 27 | ||||
-rw-r--r-- | icespider/fileSessions/Jamfile.jam | 13 | ||||
-rw-r--r-- | icespider/fileSessions/fileSessions.cpp | 132 | ||||
-rw-r--r-- | icespider/unittests/Jamfile.jam | 14 | ||||
-rw-r--r-- | icespider/unittests/testFileSessions.cpp | 112 |
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)); +} + |