[cpp-sp] branch main updated: File-backed session impl, cache refinements.
Scott Cantor
cantor.2 at osu.edu
Thu Jun 5 18:05:24 UTC 2025
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository cpp-sp.
View the commit online:
http://git.shibboleth.net/view/?p=cpp-sp.git;a=commit;h=d4536ed746c4c013aaca3c6d47bad63ea45d1ee8
The following commit(s) were added to refs/heads/main by this push:
new d4536ed7 File-backed session impl, cache refinements.
d4536ed7 is described below
commit d4536ed746c4c013aaca3c6d47bad63ea45d1ee8
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu Jun 5 14:05:16 2025 -0400
File-backed session impl, cache refinements.
---
shibsp/impl/DefaultAgent.cpp | 2 +-
shibsp/session/AbstractSessionCache.h | 2 +-
shibsp/session/SessionCacheSPI.h | 8 +-
shibsp/session/impl/AbstractSessionCache.cpp | 58 ++++---
shibsp/session/impl/FilesystemSessionCache.cpp | 176 +++++++++++++++++++--
shibsp/session/impl/MemorySessionCache.cpp | 8 +-
shibsp/util/Misc.cpp | 18 ++-
shibsp/util/Misc.h | 18 ++-
tests/data/session/impl/filesystem-shibboleth.ini | 1 +
tests/session/impl/FilesystemSessionCacheTests.cpp | 56 +++++++
10 files changed, 308 insertions(+), 39 deletions(-)
diff --git a/shibsp/impl/DefaultAgent.cpp b/shibsp/impl/DefaultAgent.cpp
index 11601fb7..93f0f74e 100644
--- a/shibsp/impl/DefaultAgent.cpp
+++ b/shibsp/impl/DefaultAgent.cpp
@@ -270,7 +270,7 @@ void DefaultAgent::doAttributeConfigurations()
} else {
string path("attributes.ini");
AgentConfig::getConfig().getPathResolver().resolve(path, PathResolver::SHIBSP_CFG_FILE);
- if (file_exists(path.c_str())) {
+ if (FileSupport::exists(path.c_str())) {
m_attributeConfigurations["default"] = AttributeConfiguration::newAttributeConfiguration(path.c_str());
m_log.info("installed 'default' AttributeConfiguration from %s", path.c_str());
} else {
diff --git a/shibsp/session/AbstractSessionCache.h b/shibsp/session/AbstractSessionCache.h
index 7b91c719..65af298e 100644
--- a/shibsp/session/AbstractSessionCache.h
+++ b/shibsp/session/AbstractSessionCache.h
@@ -71,7 +71,7 @@ namespace shibsp {
std::map<std::string,DDF> m_attributes;
AbstractSessionCache& m_cache;
- time_t m_creation,m_lastAccess,m_lastAccessReported;
+ time_t m_lastAccess,m_lastAccessReported;
// TODO: possibly convert to a shared lock where possible?
// I used exclusive because it avoided lock "upgrades"
// when mutating or deleting sessions.
diff --git a/shibsp/session/SessionCacheSPI.h b/shibsp/session/SessionCacheSPI.h
index 6d446f27..f791bfde 100644
--- a/shibsp/session/SessionCacheSPI.h
+++ b/shibsp/session/SessionCacheSPI.h
@@ -33,6 +33,10 @@ namespace shibsp {
/**
* Interface to the "back-end" persistence mechanism to allow sessions to exist
* independently of a specific agent process.
+ *
+ * <p>As a general rule, implementations should log errors internally and raise exceptions
+ * such that the caller need not log the resulting object to ensure adequate logging of the
+ * outcome.</p>
*/
class SHIBSP_API SessionCacheSPI
{
@@ -85,7 +89,7 @@ namespace shibsp {
unsigned int lifetime=0,
unsigned int timeout=0,
const char* client_addr=nullptr
- ) const=0;
+ )=0;
/**
* Informs the storage medium that a session was used at the current point in time.
@@ -99,7 +103,7 @@ namespace shibsp {
*
* @return true iff the session remains valid/available
*/
- virtual bool cache_touch(SPRequest* request, const char* key, unsigned int timeout=0) const=0;
+ virtual bool cache_touch(SPRequest* request, const char* key, unsigned int timeout=0)=0;
/**
* Delete a session record from the underlying storage medium.
diff --git a/shibsp/session/impl/AbstractSessionCache.cpp b/shibsp/session/impl/AbstractSessionCache.cpp
index efeba049..86e94c6c 100644
--- a/shibsp/session/impl/AbstractSessionCache.cpp
+++ b/shibsp/session/impl/AbstractSessionCache.cpp
@@ -160,8 +160,9 @@ string AbstractSessionCache::create(SPRequest& request, DDF& session)
// Add additional fields managed by agent.
// attributes and data members should be present from hub.
- session.addmember("creation").longinteger(time(nullptr));
- session.addmember("app_id").string(request.getRequestSettings().first->getString("applicationId"));
+ session.addmember("ts").longinteger(time(nullptr));
+ session.addmember("app_id").string(request.getRequestSettings().first->getString(
+ RequestMapper::APPLICATION_ID_PROP_NAME, RequestMapper::APPLICATION_ID_PROP_DEFAULT));
session.addmember("addr").string(request.getRemoteAddr());
// Write the data to the back-end, obtaining a key.
@@ -170,8 +171,8 @@ string AbstractSessionCache::create(SPRequest& request, DDF& session)
m_log.debug("writing new session to persistent store");
key = cache_create(&request, session);
}
- catch (const IOException& ex) {
- m_log.error("IOException writing new session to persistent store: %s", ex.what());
+ catch (const exception& ex) {
+ // Should be logged by the SPI.
session.destroy();
return string();
}
@@ -276,14 +277,15 @@ unique_lock<Session> AbstractSessionCache::_find(
// Cross-check application.
if (strcmp(applicationId, session.mutex()->getApplicationID())) {
m_log.warn("session (%s) issued for application (%s), accessed via application (%s)",
- key, applicationId, session.mutex()->getApplicationID());
+ key, session.mutex()->getApplicationID(), applicationId);
session.unlock();
}
else if (!dynamic_cast<BasicSession*>(session.mutex())->isValid(request, lifetime, timeout, client_addr)) {
// Locally invalid on its face, so remove and return nothing.
session.unlock();
m_log.debug("session (%s) invalid, removing it", key);
- remove(key);
+ // The record should be gone from the back-end but we need to dump it locally.
+ dormant(string(key));
}
// Return locked session or empty wrapper.
@@ -300,7 +302,7 @@ unique_lock<Session> AbstractSessionCache::_find(
obj = cache_read(request, applicationId, key, lifetime, timeout, client_addr);
}
catch (const exception& ex) {
- m_log.error("error reading session (%s) from persistent store: %s", key, ex.what());
+ // Should be logged by the SPI.
return unique_lock<Session>();
}
@@ -355,7 +357,12 @@ void AbstractSessionCache::remove(SPRequest& request)
return;
}
dormant(string(key));
- cache_remove(&request, key);
+ try {
+ cache_remove(&request, key);
+ }
+ catch (const exception& ex) {
+ // Should be logged by the SPI.
+ }
m_cookieManager->unsetCookie(request);
}
@@ -454,7 +461,7 @@ void* AbstractSessionCache::cleanup_fn(void* p)
if (!stale_keys.empty()) {
pcache->m_log.info("purging %u old sessions", stale_keys.size());
- // Pass 2: walk through the list of stale entries and remove them from the cache
+ // Pass 2: walk through the list of stale entries and remove them from the local cache.
for (const string& key : stale_keys) {
pcache->dormant(key.c_str());
}
@@ -469,10 +476,8 @@ void* AbstractSessionCache::cleanup_fn(void* p)
}
BasicSession::BasicSession(AbstractSessionCache& cache, DDF& obj)
- : m_obj(obj), m_cache(cache), m_creation(0), m_lastAccess(time(nullptr)), m_lastAccessReported(m_lastAccess)
+ : m_obj(obj), m_cache(cache), m_lastAccess(time(nullptr)), m_lastAccessReported(m_lastAccess)
{
- m_creation = m_obj["creation"].longinteger();
-
// This is safe to directly expose for iteration of the attributes
// as long as we maintain the mutex-based lock approach for exclusive
// Session access. If we made that a shared lock, this all has to change.
@@ -519,6 +524,7 @@ bool BasicSession::isValid(SPRequest* request, unsigned int lifetime, unsigned i
// TODO: Implement the fuzzy address matching.
if (client_addr && strcmp(client_addr, getClientAddress())) {
m_cache.log().warn("session (%s) invalid, bound to address (%s), accessed from (%s)", getID(), getClientAddress(), client_addr);
+ m_cache.cache_remove(request, getID());
return false;
}
@@ -532,6 +538,7 @@ bool BasicSession::isValid(SPRequest* request, unsigned int lifetime, unsigned i
string expired(date::format("%FT%TZ", chrono::system_clock::from_time_t(getCreation() + lifetime)));
m_cache.log().warn("session (%s) has expired, created (%s), expired (%s)", getID(), created.c_str(), expired.c_str());
}
+ m_cache.cache_remove(request, getID());
return false;
}
}
@@ -544,8 +551,15 @@ bool BasicSession::isValid(SPRequest* request, unsigned int lifetime, unsigned i
if (m_lastAccess - m_lastAccessReported > m_cache.m_storageAccessInterval) {
// It's been X seconds since we last wrote through to storage...
- if (!m_cache.cache_touch(request, getID(), timeout)) {
- m_cache.log().warn("session (%) missing or invalid in persistent store, invalidating locally", getID());
+ try {
+ // Pass a zero to bypass timeout enforcement as we know as well or better than the back-end...
+ if (!m_cache.cache_touch(request, getID(), 0)) {
+ m_cache.log().warn("session (%) missing in persistent store, invalidating locally", getID());
+ return false;
+ }
+ }
+ catch (const exception& ex) {
+ // Should be logged by the SPI.
return false;
}
// Update reporting timestamp.
@@ -553,10 +567,16 @@ bool BasicSession::isValid(SPRequest* request, unsigned int lifetime, unsigned i
}
}
else {
- // The session is locally invalid due to inactivity, but this isn't "truth" because other agent processes may
- // actively be using it.
- if (!m_cache.cache_touch(request, getID(), timeout)) {
- m_cache.log().warn("session (%s) timed out due to inactivity", getID());
+ try {
+ // The session is locally invalid due to inactivity, but this isn't "truth" because other agent processes may
+ // actively be using it.
+ if (!m_cache.cache_touch(request, getID(), timeout)) {
+ m_cache.log().warn("session (%s) timed out due to inactivity", getID());
+ return false;
+ }
+ }
+ catch (const exception& ex) {
+ // Should be logged by the SPI.
return false;
}
// Update reporting timestamp.
@@ -571,7 +591,7 @@ bool BasicSession::isValid(SPRequest* request, unsigned int lifetime, unsigned i
time_t BasicSession::getCreation() const
{
- return m_creation;
+ return m_obj["ts"].longinteger();
}
time_t BasicSession::getLastAccess() const
diff --git a/shibsp/session/impl/FilesystemSessionCache.cpp b/shibsp/session/impl/FilesystemSessionCache.cpp
index e35ed369..056591e2 100644
--- a/shibsp/session/impl/FilesystemSessionCache.cpp
+++ b/shibsp/session/impl/FilesystemSessionCache.cpp
@@ -24,12 +24,19 @@
#include "csprng/csprng.hpp"
#include "session/AbstractSessionCache.h"
#include "logging/Category.h"
+#include "util/Date.h"
#include "util/Misc.h"
#include "util/PathResolver.h"
#include <cstdio>
#include <fstream>
+#ifdef WIN32
+# include <sys/utime.h>
+#else
+# include <utime.h>
+#endif
+
#include <boost/property_tree/ptree.hpp>
using namespace shibsp;
@@ -50,11 +57,12 @@ namespace {
unsigned int lifetime=0,
unsigned int timeout=0,
const char* client_addr=nullptr
- ) const;
- bool cache_touch(SPRequest* request, const char* key, unsigned int timeout=0) const;
+ );
+ bool cache_touch(SPRequest* request, const char* key, unsigned int timeout=0);
void cache_remove(SPRequest* request, const char* key);
private:
+ Category& m_spilog;
string m_dir;
duthomhas::csprng m_rng;
};
@@ -69,12 +77,16 @@ namespace shibsp {
}
}
-FilesystemSessionCache::FilesystemSessionCache(const ptree& pt) : AbstractSessionCache(pt)
+FilesystemSessionCache::FilesystemSessionCache(const ptree& pt)
+ : AbstractSessionCache(pt), m_spilog(Category::getInstance(SHIBSP_LOGCAT ".SessionCache.Filesystem"))
{
m_dir = getString(CACHE_DIRECTORY_PROP_NAME, CACHE_DIRECTORY_PROP_DEFAULT);
AgentConfig::getConfig().getPathResolver().resolve(m_dir, PathResolver::SHIBSP_CACHE_FILE);
+ if (m_dir.back() != '/') {
+ m_dir += '/';
+ }
- string testPath = m_dir + '/' + hex_encode(m_rng(string(16,0)));
+ string testPath = m_dir + hex_encode(m_rng(string(16,0)));
bool failed = true;
@@ -99,7 +111,7 @@ FilesystemSessionCache::FilesystemSessionCache(const ptree& pt) : AbstractSessio
std::remove(testPath.c_str());
if (failed) {
- log().error("could not perform read/write in cache directory (%s), check permissions", m_dir.c_str());
+ m_spilog.error("could not perform read/write in cache directory (%s), check permissions", m_dir.c_str());
throw ConfigurationException("Configured session cache directory was inaccessible to agent process.");
}
}
@@ -110,7 +122,29 @@ FilesystemSessionCache::~FilesystemSessionCache()
string FilesystemSessionCache::cache_create(SPRequest* request, DDF& sessionData)
{
- return string();
+ string key;
+ string path;
+ int attempts = 0;
+ do {
+ key = hex_encode(m_rng(string(16,0)));
+ path = m_dir + key;
+ if (!FileSupport::exists(path.c_str())) {
+ ofstream os (path);
+ if (os) {
+ os << sessionData;
+ if (os) {
+ m_spilog.debug("stored new session (%s)", key.c_str());
+ return key;
+ }
+ }
+
+ m_spilog.error("error writing new session to file (%s), errno=%d", path.c_str(), errno);
+ throw IOException("Error writing new session to file.");
+ }
+ } while (++attempts < 3);
+
+ m_spilog.error("failed to write new session after 3 attempts to generate a unique key");
+ throw IOException("Exhausted attempts to generate a unique session key.");
}
DDF FilesystemSessionCache::cache_read(
@@ -120,16 +154,138 @@ DDF FilesystemSessionCache::cache_read(
unsigned int lifetime,
unsigned int timeout,
const char* client_addr
- ) const
+ )
{
- return DDF();
+ DDF obj;
+
+ string path = m_dir + key;
+ time_t lastAccess = FileSupport::getModificationTime(path.c_str());
+ ifstream is(path);
+ if (!is) {
+ int e = errno;
+ if (e == ENOENT) {
+ m_spilog.debug("session file (%s) does not exist", path.c_str());
+ }
+ else {
+ m_spilog.error("error opening session file (%s) for reading, errno=%d", path.c_str(), e);
+ }
+ return obj;
+ }
+
+ time_t now = time(nullptr);
+
+ if (timeout) {
+ if (lastAccess == 0) {
+ m_spilog.error("timeout specified, but unable to obtain mod time for file (%s)", path.c_str());
+ return obj;
+ }
+ else if (lastAccess + timeout < now) {
+ if (m_spilog.isInfoEnabled()) {
+ string ts(date::format("%FT%TZ", chrono::system_clock::from_time_t(lastAccess)));
+ m_spilog.info("session (%s) expired for inactivity, timeout (%lu), last access (%s)", key, timeout, ts.c_str());
+ cache_remove(request, key);
+ }
+ return obj;
+ }
+ }
+
+ is >> obj;
+ is.close();
+ if (obj.isnull()) {
+ m_spilog.error("error deserializing session from file (%s)", path.c_str());
+ return obj;
+ } else if (!obj.isstruct()) {
+ obj.destroy();
+ m_spilog.error("deserialized session from file (%s) was invalid", path.c_str());
+ return obj;
+ }
+
+ const char* appId = obj["appId"].string();
+ if (strcmp(applicationId, appId)) {
+ m_spilog.warn("session (%s) issued for application (%s), accessed via application (%s)", key, appId, applicationId);
+ obj.destroy();
+ return obj;
+ }
+
+ // TODO: Implement the fuzzy address matching.
+ if (client_addr) {
+ const char* addr = obj["addr"].string();
+ if (addr && strcmp(client_addr, addr)) {
+ m_spilog.warn("session (%s) invalid, bound to address (%s), accessed from (%s)", key, addr, client_addr);
+ obj.destroy();
+ cache_remove(request, key);
+ return obj;
+ }
+ }
+
+ if (lifetime) {
+ time_t start = obj["ts"].longinteger();
+ if (start + lifetime < now) {
+ obj.destroy();
+ if (m_spilog.isWarnEnabled()) {
+ string created(date::format("%FT%TZ", chrono::system_clock::from_time_t(start)));
+ string expired(date::format("%FT%TZ", chrono::system_clock::from_time_t(start + lifetime)));
+ m_spilog.info("session (%s) has expired, created (%s), expired (%s)", key, created.c_str(), expired.c_str());
+ }
+ cache_remove(request, key);
+ return obj;
+ }
+ }
+
+ return obj;
}
-bool FilesystemSessionCache::cache_touch(SPRequest* request, const char* key, unsigned int timeout) const
+bool FilesystemSessionCache::cache_touch(SPRequest* request, const char* key, unsigned int timeout)
{
- return false;
+ string path = m_dir + key;
+ if (timeout) {
+ time_t lastAccess = FileSupport::getModificationTime(path.c_str());
+ if (lastAccess == 0) {
+ m_spilog.error("timeout specified, but unable to obtain mod time for file (%s)", path.c_str());
+ cache_remove(request, key);
+ return false;
+ }
+ else if (lastAccess + timeout < time(nullptr)) {
+ if (m_spilog.isInfoEnabled()) {
+ string ts(date::format("%FT%TZ", chrono::system_clock::from_time_t(lastAccess)));
+ m_spilog.info("session (%s) expired for inactivity, timeout (%lu), last access (%s)", key, timeout, ts.c_str());
+ }
+ cache_remove(request, key);
+ return false;
+ }
+ }
+
+#ifdef WIN32
+ if (_utime(path.c_str(), nullptr) != 0) {
+#else
+ if (utime(path.c_str(), nullptr) != 0) {
+#endif
+ int e = errno;
+ if (e == ENOENT) {
+ m_spilog.debug("unable to update access time, session file (%s) did not exist", path.c_str());
+ return false;
+ }
+ else {
+ m_spilog.error("unable to update access time for session (%s), errno=%d", path.c_str(), e);
+ // Debatable if we fall into returning true, but maybe we don't care?
+ }
+ }
+ return true;
}
void FilesystemSessionCache::cache_remove(SPRequest* request, const char* key)
{
+ string path = m_dir + key;
+ if (std::remove(path.c_str()) != 0) {
+ int e = errno;
+ if (e == ENOENT) {
+ m_spilog.debug("session file (%s) did not exist", path.c_str());
+ }
+ else {
+ m_spilog.error("error removing file for session (%s), errno=%d", key, e);
+ }
+ }
+ else {
+ m_spilog.debug("removed session (%s)", key);
+ }
}
diff --git a/shibsp/session/impl/MemorySessionCache.cpp b/shibsp/session/impl/MemorySessionCache.cpp
index 4306cde8..77dafd1a 100644
--- a/shibsp/session/impl/MemorySessionCache.cpp
+++ b/shibsp/session/impl/MemorySessionCache.cpp
@@ -49,8 +49,8 @@ namespace {
unsigned int lifetime=0,
unsigned int timeout=0,
const char* client_addr=nullptr
- ) const;
- bool cache_touch(SPRequest* request, const char* key, unsigned int timeout=0) const;
+ );
+ bool cache_touch(SPRequest* request, const char* key, unsigned int timeout=0);
void cache_remove(SPRequest* request, const char* key);
private:
@@ -84,12 +84,12 @@ DDF MemorySessionCache::cache_read(
unsigned int lifetime,
unsigned int timeout,
const char* client_addr
- ) const
+ )
{
return DDF();
}
-bool MemorySessionCache::cache_touch(SPRequest* request, const char* key, unsigned int timeout) const
+bool MemorySessionCache::cache_touch(SPRequest* request, const char* key, unsigned int timeout)
{
return true;
}
diff --git a/shibsp/util/Misc.cpp b/shibsp/util/Misc.cpp
index f96632b4..46d1c3c1 100644
--- a/shibsp/util/Misc.cpp
+++ b/shibsp/util/Misc.cpp
@@ -50,7 +50,7 @@ set<string>::size_type shibsp::split_to_container(set<string>& container, const
return container.size();
}
-bool shibsp::file_exists(const char* path)
+bool FileSupport::exists(const char* path)
{
#ifdef WIN32
struct _stat stat_buf;
@@ -65,3 +65,19 @@ bool shibsp::file_exists(const char* path)
#endif
return false;
}
+
+time_t FileSupport::getModificationTime(const char* path)
+{
+#ifdef WIN32
+ struct _stat stat_buf;
+ if (_stat(path, &stat_buf) == 0) {
+ return stat_buf.st_mtime;
+ }
+#else
+ struct stat stat_buf;
+ if (stat(path, &stat_buf) == 0) {
+ return stat_buf.st_mtime;
+ }
+#endif
+ return 0;
+}
diff --git a/shibsp/util/Misc.h b/shibsp/util/Misc.h
index df62612b..441b42a1 100644
--- a/shibsp/util/Misc.h
+++ b/shibsp/util/Misc.h
@@ -83,7 +83,23 @@ namespace shibsp {
}
};
- bool file_exists(const char* path);
+ struct FileSupport {
+ /**
+ * Checks whether a file exists.
+ *
+ * @param path path to check
+ */
+ static bool exists(const char* path);
+
+ /**
+ * Gets the last time the file was modified.
+ *
+ * @param path path to check
+ *
+ * @return modification time or 0 if unable
+ */
+ static time_t getModificationTime(const char* path);
+ };
/**
* Splitter functions that trim the input and split on whitespace into a container.
diff --git a/tests/data/session/impl/filesystem-shibboleth.ini b/tests/data/session/impl/filesystem-shibboleth.ini
index 281d1621..a0161c93 100644
--- a/tests/data/session/impl/filesystem-shibboleth.ini
+++ b/tests/data/session/impl/filesystem-shibboleth.ini
@@ -9,6 +9,7 @@ defaultLevel = INFO
[logging-categories]
Shibboleth.SessionCache = DEBUG
+Shibboleth.SessionCache.Filesystem = DEBUG
[session-cache]
type = filesystem
diff --git a/tests/session/impl/FilesystemSessionCacheTests.cpp b/tests/session/impl/FilesystemSessionCacheTests.cpp
index 55618995..430a10b3 100644
--- a/tests/session/impl/FilesystemSessionCacheTests.cpp
+++ b/tests/session/impl/FilesystemSessionCacheTests.cpp
@@ -75,6 +75,62 @@ struct FilesystemFixture
BOOST_FIXTURE_TEST_CASE(FilesystemSessionCache_tests, FilesystemFixture)
{
+ bool started = AgentConfig::getConfig().start();
+ BOOST_CHECK(started);
+
+ DDF obj(nullptr);
+ DDFJanitor janitor(obj);
+
+ obj.addmember("session.opaque").string("foo");
+ DDF attrs = obj.addmember("session.attributes").list();
+
+ DDF issuer("Shib-Identity-Provider");
+ issuer.list();
+ issuer.add(DDF(nullptr).string("https://idp.example.org"));
+ attrs.add(issuer);
+
+ DDF affiliation("affiliation");
+ affiliation.list();
+ affiliation.add(DDF(nullptr).string("member"));
+ affiliation.add(DDF(nullptr).string("student"));
+ attrs.add(affiliation);
+
+ DummyRequest request("https://sp.example.org/secure/index.html");
+ DDF child = obj["session"];
+
+ SessionCache* cache = AgentConfig::getConfig().getAgent().getSessionCache();
+
+ string key = cache->create(request, child);
+
+ BOOST_CHECK(obj["session"].isnull());
+ BOOST_CHECK_EQUAL(key.c_str(), child.name());
+ string cookieName("__Host-shibsession_73702e6578616d706c652e6f7267637573746f6d");
+ string header(cookieName);
+ header += '=' + key;
+ header += "; max-age=-1; path=/; secure=1; HttpOnly=1; SameSite=None";
+ BOOST_CHECK_EQUAL(request.m_responseHeaders["Set-Cookie"], header);
+
+ string cookie(cookieName);
+ cookie += '=' + key;
+ request.m_requestHeaders["Cookie"] = cookie;
+
+ unique_lock<Session> session = cache->find(request, true, false);
+ BOOST_CHECK(session);
+ if (session) {
+ session.unlock();
+ }
+
+ // Clear old response headers.
+ request.m_responseHeaders.clear();
+
+ cache->remove(request);
+
+ header = cookieName;
+ header += "=; max-age=0; path=/; secure=1; HttpOnly=1; SameSite=None";
+ BOOST_CHECK_EQUAL(request.m_responseHeaders["Set-Cookie"], header);
+
+ session = cache->find("custom", key.c_str());
+ BOOST_CHECK(!session);
}
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list