Feature #7598 » 0001-Add-framework-for-weakSessionId-support.patch
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);
|
||