Merge JSON-156 into default.

JSON-170 Release_2.2.0-RC1
Jorg Knobloch 2 years ago
commit d031b66d50

@ -22,7 +22,7 @@ bool Context::verify_security_token(const std::string& token) const
// Cache a certain function call. See JSON-155.
void Context::cache(const std::string& func_name, const std::function<void(PEP_SESSION)>& fn)
{
ja->cache(func_name, fn);
ja->cache(client_id(), func_name, fn);
}

@ -12,11 +12,15 @@ class JsonAdapterBase;
class Context
{
public:
Context(JsonAdapterBase* _ja) : ja{_ja} {}
Context(JsonAdapterBase* _ja, const std::string& _cid)
: ja{_ja}, cid{_cid}
{}
Context(const Context&) = delete;
void operator=(const Context&) = delete;
const std::string& client_id() const { return cid; }
// delegate call to the 'ja' member
bool verify_security_token(const std::string& token) const ;
@ -29,11 +33,11 @@ public:
// KISS: at the moment only "size_t" objects are supported.
void store(int position, size_t value);
size_t retrieve(int position);
void clear();
private:
std::map<int, size_t> obj_store;
JsonAdapterBase* ja;
const std::string& cid;
};
#endif // JSON_ADAPTER_CONTEXT_HH

@ -100,6 +100,8 @@ struct JsonAdapter::Internal
bool ignore_session_error = false;
bool deliver_html = true;
int client_session_timeout = 7*60; // in seconds
explicit Internal()
: Log("JAI")
{}
@ -152,15 +154,15 @@ struct JsonAdapter::Internal
};
PEP_SESSION JsonAdapter::getSessionForThread()
PEP_SESSION JsonAdapter::getSessionForThread(const std::string& client_id)
{
const auto id = std::this_thread::get_id();
return JsonAdapter::getInstance().i->session_registry->get(id);
const auto thread_id = std::this_thread::get_id();
return JsonAdapter::getInstance().i->session_registry->get(thread_id, client_id);
}
In_Pep_Session::In_Pep_Session(const js::Value& v, Context*, unsigned)
: Base( JsonAdapter::getSessionForThread() )
In_Pep_Session::In_Pep_Session(const js::Value& v, Context* ctx, unsigned)
: Base( JsonAdapter::getSessionForThread(ctx->client_id()) )
{}
@ -244,11 +246,19 @@ JsonAdapter& JsonAdapter::deliver_html(bool dh)
}
JsonAdapter& JsonAdapter::set_client_session_timeout(int timeout_seconds)
{
check_guard();
i->client_session_timeout = timeout_seconds;
return *this;
}
void JsonAdapter::prepare_run(const std::string& address, unsigned start_port, unsigned end_port, ::messageToSend_t messageToSend)
{
check_guard();
// delayed after constructor, so virtual functions are working:
i->session_registry.reset(new SessionRegistry(messageToSend ? messageToSend : this->getMessageToSend(), this->getInjectSyncEvent()));
i->session_registry.reset(new SessionRegistry(messageToSend ? messageToSend : this->getMessageToSend(), this->getInjectSyncEvent(), i->client_session_timeout));
for(unsigned short port = start_port; port<=end_port; ++port)
{
@ -355,9 +365,9 @@ bool JsonAdapter::verify_security_token(const std::string& s) const
}
void JsonAdapter::cache(const std::string& fn_name, const std::function<void(PEP_SESSION)>& func)
void JsonAdapter::cache(const std::string& client_id, const std::string& fn_name, const std::function<void(PEP_SESSION)>& func)
{
i->session_registry->add_to_cache(fn_name, func);
i->session_registry->add_to_cache(client_id, fn_name, func);
}

@ -21,7 +21,7 @@ public:
virtual bool verify_security_token(const std::string& s) const = 0;
// Cache a certain function call. See JSON-155.
virtual void cache(const std::string& fn_name, const std::function<void(PEP_SESSION)>& func) = 0;
virtual void cache(const std::string& client_id, const std::string& fn_name, const std::function<void(PEP_SESSION)>& func) = 0;
};
@ -51,6 +51,9 @@ public:
// if called with "false" the JSON Adpapter would no longer deliver HTML and JavaScript files, only handle JSON-RPC requests
JsonAdapter& deliver_html(bool _deliver_html);
// sets the timeout to drop client's config cache
JsonAdapter& set_client_session_timeout(int timeout_seconds);
// look for a free port to listen on and set the given configuration
void prepare_run(const std::string& address, unsigned start_port, unsigned end_port, ::messageToSend_t messageToSend);
@ -87,14 +90,14 @@ public:
// returns 'true' if 's' is the security token created by the function above.
virtual bool verify_security_token(const std::string& s) const override;
virtual void cache(const std::string& fn_name, const std::function<void(PEP_SESSION)>& func) override;
virtual void cache(const std::string& client_id, const std::string& fn_name, const std::function<void(PEP_SESSION)>& func) override;
// returns the version of the JsonAdapter
static
ServerVersion version();
// returns the PEP_SESSION registered for the current thread
static PEP_SESSION getSessionForThread();
static PEP_SESSION getSessionForThread(const std::string& client_id);
static PEP_STATUS messageToSend(message* msg);
static PEP_STATUS notifyHandshake(pEp_identity* self, pEp_identity* partner, sync_handshake_signal signal);

@ -131,7 +131,7 @@ js::Object call(const FunctionMap& fm, const js::Object& request, JsonAdapterBas
DEBUG_OUT(L, "method_name=\"" + method_name + "\"\n"
"params=" + js::write(params) );
Context context{ja};
Context context{ja, client_id_s};
const js::Value result = fn->second->call(p, &context);
DEBUG_OUT(L, "result=" + js::write(result, js::raw_utf8) );

@ -27,6 +27,7 @@ bool do_sync = false;
bool ignore_missing_session = false;
bool add_sharks = false;
bool no_html = false;
int client_session_timeout = 7*60; // in secondds
uintptr_t status_handle = 0;
@ -76,6 +77,7 @@ try
("ignore-missing-session", po::bool_switch(&ignore_missing_session), "Ignore when no PEP_SESSION can be created.")
("add-sharks", po::bool_switch(&add_sharks), "Add sharks to the JSON Adapter.")
("no-html" , po::bool_switch(&no_html ), "Don't deliver HTML and JavaScript files, only accept JSON-RPC calls.")
("client-timeout", po::value<int>(&client_session_timeout)->default_value(client_session_timeout), "Drop cached client session config after this timeout (in seconds).")
#ifdef _WIN32
((STATUS_HANDLE), po::value<uintptr_t>(&status_handle)->default_value(0), "Status file handle, for internal use.")
#endif
@ -144,6 +146,7 @@ try
JsonAdapter& ja = pEp::mini::Adapter::createInstance();
ja.ignore_session_errors( ignore_missing_session)
.deliver_html( !no_html )
.set_client_session_timeout(client_session_timeout)
;
/*
* FIXME: why are exceptions risen after the instantiation of JsonAdapter

@ -76,7 +76,8 @@ static const std::string VersionName =
// "(42) Gotha"; // JSON-152: 2-parameter version of pollForEvents().
// "(43) Wandersleben"; // JSON-153 passphrase support. *sigh*
// "(44) Neudietendorf"; // replace my own sync thread code by libpEpAdapter's implementation.
"(45) Kreuz Erfurt"; // fix of context-saved function parameters that would cause trouble when >1 request is processed in parallel.
// "(45) Kreuz Erfurt"; // fix of context-saved function parameters that would cause trouble when >1 request is processed in parallel.
"(46) Erfurt-West"; // JSON-156: delete client cached values after timeout.
} // end of anonymous namespace
////////////////////////////////////////////////////////////////////////////

@ -6,10 +6,12 @@
// creates a PEP_SESSION if none yet exists for the given thread
PEP_SESSION SessionRegistry::get(std::thread::id tid)
PEP_SESSION SessionRegistry::get(std::thread::id tid, const std::string& client_id)
{
Lock L(_mtx);
update_last_use(client_id);
auto q = m.find(tid);
if(q != m.end())
{
@ -24,8 +26,10 @@ PEP_SESSION SessionRegistry::get(std::thread::id tid)
throw std::runtime_error("init() fails: " + pEp::status_to_string(status) );
}
m[tid] = session;
Log.debug("Apply %zu cached config values to new session.", cache.size());
for(const auto& e : cache)
const auto& cache_for_client = cache[client_id];
Log.debug("Apply %zu cached config values for client_id \"%s\" to new session.", cache_for_client.size(), client_id.c_str());
for(const auto& e : cache_for_client)
{
Log.debug("\t %s", e.first.c_str());
e.second(session);
@ -62,11 +66,12 @@ void SessionRegistry::for_each(void(*function)(PEP_SESSION))
}
void SessionRegistry::add_to_cache(const std::string& fn_name, const std::function<void(PEP_SESSION)>& func)
void SessionRegistry::add_to_cache(const std::string& client_id, const std::string& fn_name, const std::function<void(PEP_SESSION)>& func)
{
Lock L(_mtx);
Log.debug("add_to_cache(\"%s\")", fn_name.c_str());
cache[fn_name] = func;
Log.debug("add_to_cache(\"%s\", \"%s\")", client_id.c_str(), fn_name.c_str());
cache[client_id][fn_name] = func;
update_last_use(client_id);
}
@ -81,3 +86,23 @@ std::string SessionRegistry::to_string() const
}
return ss.str();
}
void SessionRegistry::update_last_use(const std::string& client_id)
{
const auto now = std::chrono::system_clock::now();
const auto too_old = now - std::chrono::seconds(client_timeout);
last_use[client_id] = now;
// TODO: replace by C++20 std::erase_if()
for(auto q = last_use.begin(); q != last_use.end(); /* no increment here */ )
{
if(q->second < too_old)
{
cache.erase( q->first );
q = last_use.erase(q);
}else{
++q;
}
}
}

@ -1,6 +1,7 @@
#ifndef JSON_ADAPTER_SESSION_REGISTRY_HH
#define JSON_ADAPTER_SESSION_REGISTRY_HH
#include <chrono>
#include <map>
#include <mutex>
#include <thread>
@ -11,14 +12,15 @@
class SessionRegistry
{
public:
SessionRegistry(messageToSend_t _mts, inject_sync_event_t _ise)
SessionRegistry(messageToSend_t _mts, inject_sync_event_t _ise, int _client_timeout)
: mts{_mts}
, ise{_ise}
, Log{"SR"}
, client_timeout{_client_timeout} // in seconds
{}
// calls "init" for the given thread if no PEP_SESSION exists, yet for the given thread
PEP_SESSION get(std::thread::id tid = std::this_thread::get_id());
PEP_SESSION get(std::thread::id tid, const std::string& client_id);
void remove(std::thread::id tid = std::this_thread::get_id());
std::size_t size() const { return m.size(); }
@ -28,7 +30,7 @@ public:
// on each stored session.
void for_each(void(*function)(PEP_SESSION));
void add_to_cache(const std::string& fn_name, const std::function<void(PEP_SESSION)>& func);
void add_to_cache(const std::string& client_id, const std::string& fn_name, const std::function<void(PEP_SESSION)>& func);
std::string to_string() const;
@ -36,12 +38,23 @@ private:
std::map<std::thread::id, PEP_SESSION> m;
messageToSend_t mts;
inject_sync_event_t ise;
Logger Log;
std::map<std::string, std::function<void(PEP_SESSION)>> cache;
Logger Log;
int client_timeout; // in seconds
// function name -> functor
typedef
std::map<std::string, std::function<void(PEP_SESSION)>> cache_per_client_t;
// key=client_id
std::map<std::string, cache_per_client_t> cache;
std::map<std::string, std::chrono::time_point<std::chrono::system_clock> > last_use;
typedef std::recursive_mutex Mutex;
typedef std::unique_lock<Mutex> Lock;
mutable Mutex _mtx;
void update_last_use(const std::string& client_id);
};
#endif // JSON_ADAPTER_SESSION_REGISTRY_HH

@ -30,7 +30,7 @@ class DummyAdapter : public JsonAdapterBase
public:
virtual bool verify_security_token(const std::string& token) const override { return true; }
virtual void cache(const std::string& func_name, const std::function<void(PEP_SESSION)>& fn) override
virtual void cache(const std::string& client_id, const std::string& func_name, const std::function<void(PEP_SESSION)>& fn) override
{
// do nothing
}

@ -0,0 +1,41 @@
#include <gtest/gtest.h>
#include <pEp/constant_time_algo.hh>
namespace {
const char nullo[4] = {0,0,0,0};
const char null_x[4] = { '\0', 'X', '\0', '\n' };
const std::vector<std::string> testValuesInput =
{
{ "" }, // always start with the simple case ;-)
{ "123" }, // some ASCII digits. Still easy.
{ "\n\\\b" }, // backslash escapes for ASCII and control chars
{ "äöü\x80\x7f" }, // also with some non-ASCII chars
{ std::string(nullo, nullo+1) }, // Yeah, 1 NUL byte
{ std::string(nullo, nullo+2) }, // Yeah, 2 NUL bytes
{ std::string(null_x, null_x+4) }, // guess what...
{ "EOF" }
};
}
class StringCompareTest : public ::testing::TestWithParam<std::string>
{
// intentionally left blank for now.
};
INSTANTIATE_TEST_CASE_P(StringcompareTestInstance, StringCompareTest, testing::ValuesIn(testValuesInput) );
TEST_P( StringCompareTest, Equal )
{
const auto param = GetParam();
EXPECT_TRUE( pEp::constant_time_equal(param, param) );
EXPECT_FALSE( pEp::constant_time_equal(param, "") );
EXPECT_FALSE( pEp::constant_time_equal("@@@", param) );
}
Loading…
Cancel
Save