Project

General

Profile

Feature #7598 » 0001-Add-framework-for-weakSessionId-support.patch

Bruce Toll, 06/08/2020 02:14 PM

View differences:

src/Wt/Test/WTestEnvironment.C
void WTestEnvironment::init(EntryPointType type)
{
session_ = new WebSession(controller_, "testwtd", type, "", 0, this);
session_ = new WebSession(controller_, "testwtd", "weaktestwtd", type, "", 0, this);
theSession_.reset(session_);
#ifndef WT_TARGET_JAVA
src/web/WebController.C
for (SessionMap::iterator i = sessions_.begin(); i != sessions_.end();
++i)
sessionList.push_back(i->second);
sessionList.push_back(i->session());
sessions_.clear();
......
#endif
std::vector<std::string> sessionIds;
for (SessionMap::const_iterator i = sessions_.begin(); i != sessions_.end(); ++i) {
sessionIds.push_back(i->first);
sessionIds.push_back(i->sessionId());
}
return sessionIds;
}
......
#endif // WT_THREADED
for (SessionMap::iterator i = sessions_.begin(); i != sessions_.end();) {
std::shared_ptr<WebSession> session = i->second;
std::shared_ptr<WebSession> session = i->session();
int diff = session->expireTime() - now;
......
return result;
}
/*
* addSession is called from WTestEnvironment w/hard-coded sessionId and weakSessionId
* ...so we can avoid checking that these ids are distinct from one another
*/
void WebController::addSession(const std::shared_ptr<WebSession>& session)
{
#ifdef WT_THREADED
std::unique_lock<std::recursive_mutex> lock(mutex_);
#endif // WT_THREADED
sessions_[session->sessionId()] = session;
sessions_.insert(SessionEntry(session->sessionId(), session->weakSessionId(), session));
}
void WebController::removeSession(const std::string& sessionId)
......
SessionMap::iterator i = sessions_.find(sessionId);
if (i != sessions_.end()) {
++zombieSessions_;
if (i->second->env().ajax())
if (i->session()->env().ajax())
--ajaxSessions_;
else
--plainHtmlSessions_;
......
return false;
std::string sessionId = *wtdE;
/*
* This could be a weak sessionId, but we need to post event to
* a normal sessionId. So, look it up as a weakSessionId and replace
* with the corresponding sessionId, if found...
*/
{
#ifdef WT_THREADED
std::unique_lock<std::recursive_mutex> lock(mutex_);
SessionMap::index<WeakSessionIdTag>::type::iterator iw =
sessions_.get<WeakSessionIdTag>().find(sessionId);
if (iw != sessions_.get<WeakSessionIdTag>().end()) {
sessionId = iw->sessionId();
}
#endif // WT_THREADED
}
ApplicationEvent event(sessionId,
std::bind(&WebController::updateResourceProgress,
......
SessionMap::iterator i = sessions_.find(event.sessionId);
if (i != sessions_.end() && !i->second->dead())
session = i->second;
if (i != sessions_.end() && !i->session()->dead())
session = i->session();
}
if (!session) {
......
return;
}
std::string sessionId;
std::string sessionId, weakSessionId;
/*
* Get session from request.
......
SessionMap::iterator i = sessions_.find(sessionId);
// Not a valid regular sessionId, perhaps it is a weakSessionId (used for resources)?
if (i == sessions_.end()) {
SessionMap::index<WeakSessionIdTag>::type::iterator iw =
sessions_.get<WeakSessionIdTag>().find(sessionId);
// if matched a weakSessionId, set i iterator to point at SessionElement
if (iw != sessions_.get<WeakSessionIdTag>().end()) {
i = sessions_.project<SessionIdTag>(iw);
}
}
Configuration::SessionTracking sessionTracking = configuration().sessionTracking();
if (i == sessions_.end() || i->second->dead() ||
if (i == sessions_.end() || i->session()->dead() ||
(sessionTracking == Configuration::Combined &&
(multiSessionCookie.empty() || multiSessionCookie != i->second->multiSessionId()))) {
(multiSessionCookie.empty() || multiSessionCookie != i->session()->multiSessionId()))) {
try {
if (sessionTracking == Configuration::Combined &&
i != sessions_.end() && !i->second->dead()) {
i != sessions_.end() && !i->session()->dead()) {
if (!request->headerValue("Cookie")) {
LOG_ERROR_S(&server_, "Valid session id: " << sessionId << ", but "
"no cookie received (expecting multi session cookie)");
......
}
if (singleSessionId_.empty()) {
do {
sessionId = conf_.generateSessionId();
if (!conf_.registerSessionId(std::string(), sessionId))
sessionId.clear();
} while (sessionId.empty());
if (!findAvailableSessionIds("", sessionId, weakSessionId)) {
LOG_ERROR_S(&server_, "Could not allocate session ids. Consider increasing session-id-length");
request->setStatus(500);
request->flush(WebResponse::ResponseState::ResponseDone);
return;
}
}
std::string favicon = request->entryPoint_->favicon();
if (favicon.empty())
conf_.readConfigurationProperty("favicon", favicon);
session.reset(new WebSession(this, sessionId,
session.reset(new WebSession(this, sessionId, weakSessionId,
request->entryPoint_->type(),
favicon, request));
......
+ " Path=" + session->env().deploymentPath()
+ "; httponly;" + (session->env().urlScheme() == "https" ? " secure;" : ""));
sessions_[sessionId] = session;
sessions_.insert(SessionEntry(sessionId, weakSessionId, session));
++plainHtmlSessions_;
} catch (std::exception& e) {
LOG_ERROR_S(&server_, "could not create new session: " << e.what());
......
return;
}
} else {
session = i->second;
session = i->session();
}
}
......
return conf_.matchEntryPoint(scriptName, pathInfo, false);
}
/*
* If either sessionId or WeakSessionId in/out parameter is empty,
* replace it with a newly generated sessionId -- if one is available.
* For a sessionId to be considered available, it must be:
*
* 1. unused in the SessionIdTag index to sessions_
* 2. unused in the WeakSessionIdTag index to sessions_
*
* For the SessionId, the registerSessionId method must also
* succeed (used by FCGI connector).
*
* Return true on success.
*
* NOTE: The WebController mutex_ must be held when calling this method
*/
bool WebController::findAvailableSessionIds(std::string registrationOldId,
std::string& sessionId,
std::string& weakSessionId)
{
// With default length session ids, the likelihood of any retries is very small
for (int retry = 0; weakSessionId.empty() && retry < 10; ++retry) {
weakSessionId = conf_.generateSessionId();
if (sessions_.get<WeakSessionIdTag>().find(weakSessionId) !=
sessions_.get<WeakSessionIdTag>().end()) {
weakSessionId.clear();
continue;
}
if (sessions_.find(weakSessionId) != sessions_.end())
weakSessionId.clear();
}
for (int retry = 0; sessionId.empty() && retry < 10; ++retry) {
sessionId = conf_.generateSessionId();
if (sessionId == weakSessionId) {
sessionId.clear();
continue;
}
if (sessions_.get<WeakSessionIdTag>().find(sessionId) !=
sessions_.get<WeakSessionIdTag>().end()) {
sessionId.clear();
continue;
}
if (sessions_.find(sessionId) != sessions_.end()) {
sessionId.clear();
continue;
}
if (!conf_.registerSessionId(registrationOldId, sessionId))
sessionId.clear();
}
return !sessionId.empty() && !weakSessionId.empty();
}
std::string
WebController::generateNewSessionId(const std::shared_ptr<WebSession>& session)
{
......
std::unique_lock<std::recursive_mutex> lock(mutex_);
#endif // WT_THREADED
std::string newSessionId;
do {
newSessionId = conf_.generateSessionId();
if (!conf_.registerSessionId(session->sessionId(), newSessionId))
newSessionId.clear();
} while (newSessionId.empty());
std::string newSessionId, newWeakSessionId;
if (!findAvailableSessionIds(session->sessionId(), newSessionId, newWeakSessionId)) {
throw WException(
"generateNewSessionId: out of session ids. Consider increasing session-id-length");
}
sessions_[newSessionId] = session;
sessions_.insert(SessionEntry(newSessionId, newWeakSessionId, session));
SessionMap::iterator i = sessions_.find(session->sessionId());
sessions_.erase(i);
src/web/WebController.h
#include <set>
#include <map>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <Wt/WDllDefs.h>
#include <Wt/WServer.h>
#include <Wt/WSocketNotifier.h>
......
std::string switchSession(WebSession *session,
const std::string& newSessionId);
bool findAvailableSessionIds (std::string registrationOldId,
std::string& sessionId,
std::string& weakSessionId);
std::string generateNewSessionId(const std::shared_ptr<WebSession>& session);
private:
......
#endif // WT_THREADED
std::set<std::string> uploadProgressUrls_;
typedef std::map<std::string, std::shared_ptr<WebSession> > SessionMap;
class SessionMap;
class SessionEntry {
public:
SessionEntry(std::string sessionId, std::string weakSessionId, std::shared_ptr<WebSession> session) :
sessionId_(sessionId), weakSessionId_(weakSessionId), session_(session) {}
std::string sessionId() const { return sessionId_; }
std::string weakSessionId() const { return weakSessionId_; }
std::shared_ptr<WebSession> session() const { return session_; }
private:
std::string sessionId_;
std::string weakSessionId_;
std::shared_ptr<WebSession> session_;
friend class SessionMap;
};
struct SessionIdTag { };
struct WeakSessionIdTag { };
class SessionMap : public boost::multi_index_container<
SessionEntry,
boost::multi_index::indexed_by<
boost::multi_index::ordered_unique<
boost::multi_index::tag<SessionIdTag>,
boost::multi_index::member<SessionEntry, std::string, &SessionEntry::sessionId_>
>,
boost::multi_index::hashed_unique<
boost::multi_index::tag<WeakSessionIdTag>,
boost::multi_index::member<SessionEntry, std::string, &SessionEntry::weakSessionId_>
>
>
>{ };
SessionMap sessions_;
#ifdef WT_THREADED
src/web/WebSession.C
WebSession::WebSession(WebController *controller,
const std::string& sessionId,
const std::string& weakSessionId,
EntryPointType type,
const std::string& favicon,
const WebRequest *request,
......
favicon_(favicon),
state_(State::JustCreated),
sessionId_(sessionId),
weakSessionId_(weakSessionId),
sessionIdChanged_(false),
sessionIdCookieChanged_(false),
sessionIdInUrl_(false),
......
}
#ifndef WT_TARGET_JAVA
LOG_INFO("session created (#sessions = " <<
(controller_->sessionCount() + 1) << ")");
LOG_INFO("session created (#sessions = " << (controller_->sessionCount() + 1)
<< "), sessionId: " << sessionId_ << ", weakSessionId: " << weakSessionId_);
expire_ = Time() + 60*1000;
#endif // WT_TARGET_JAVA
......
}
}
std::string WebSession::sessionQuery() const
std::string WebSession::sessionQuery(SessionOption sessionOption) const
{
std::string result ="?wtd=" + DomElement::urlEncodeS(sessionId_);
const std::string& sessionId =
(sessionOption == SessionOption::UseSessionId ? sessionId_ : weakSessionId_);
std::string result ="?wtd=" + DomElement::urlEncodeS(sessionId);
if (type() == EntryPointType::WidgetSet)
result += "&wtt=widgetset";
return result;
......
}
}
std::string WebSession::mostRelativeUrl(const std::string& internalPath) const
std::string WebSession::mostRelativeUrl(const std::string& internalPath,
SessionOption sessionOption) const
{
return appendSessionQuery(bookmarkUrl(internalPath));
return appendSessionQuery(bookmarkUrl(internalPath), sessionOption);
}
std::string WebSession::appendSessionQuery(const std::string& url) const
std::string WebSession::appendSessionQuery(const std::string& url,
SessionOption sessionOption) const
{
std::string result = url;
......
std::size_t questionPos = result.find('?');
if (questionPos == std::string::npos)
result += sessionQuery();
result += sessionQuery(sessionOption);
else if (questionPos == result.length() - 1)
result += sessionQuery().substr(1);
result += sessionQuery(sessionOption).substr(1);
else
result += '&' + sessionQuery().substr(1);
result += '&' + sessionQuery(sessionOption).substr(1);
#ifndef WT_TARGET_JAVA
return result;
......
sessionId_ = controller_->generateNewSessionId(shared_from_this());
sessionIdChanged_ = true;
LOG_INFO("new session id for " << oldId);
LOG_INFO("new session id for: " << oldId << ", new weak session id: " << weakSessionId_);
if (!useUrlRewriting()) {
std::string cookieName = env_->deploymentPath();
src/web/WebSession.h
Dead
};
WebSession(WebController *controller, const std::string& sessionId,
WebSession(WebController *controller,
const std::string& sessionId, const std::string& weakSessionId,
EntryPointType type, const std::string& favicon,
const WebRequest *request, WEnvironment *env = nullptr);
~WebSession();
......
std::string docType() const;
std::string sessionId() const { return sessionId_; }
std::string weakSessionId() const { return weakSessionId_; }
std::string multiSessionId() const { return multiSessionId_; }
void setMultiSessionId(const std::string &multiSessionId);
......
void setPagePathInfo(const std::string& path);
std::string pagePathInfo() const { return pagePathInfo_; }
enum class SessionOption {
UseSessionId,
UseWeakSessionId
};
// (http://www.bigapp.com/myapp/app.wt) ?wtd=ABCD
// or (http://www.bigapp.com/myapp/) app.wt/path?wtd=ABCD
std::string mostRelativeUrl(const std::string& internalPath = std::string())
const;
std::string mostRelativeUrl(const std::string& internalPath = std::string(),
SessionOption sessionOption = SessionOption::UseSessionId) const;
std::string appendInternalPath(const std::string& url,
const std::string& internalPath) const;
std::string appendSessionQuery(const std::string& url) const;
std::string appendSessionQuery(const std::string& url,
SessionOption sessionOption = SessionOption::UseSessionId) const;
std::string ajaxCanonicalUrl(const WebResponse& request) const;
......
std::string favicon_;
State state_;
std::string sessionId_, sessionIdCookie_, multiSessionId_;
std::string sessionId_, weakSessionId_, sessionIdCookie_, multiSessionId_;
bool sessionIdChanged_, sessionIdCookieChanged_, sessionIdInUrl_;
WebController *controller_;
......
void init(const WebRequest& request);
bool start(WebResponse *response);
std::string sessionQuery() const;
std::string sessionQuery(
SessionOption sessionOption = SessionOption::UseSessionId) const;
void flushBootStyleResponse();
void changeInternalPath(const std::string& path, WebResponse *response);
(1-1/4)