Handle websocket autoconnect and remove session duplicates. Resolves #1851. Resolves #1562.

This commit is contained in:
Griatch 2019-06-15 22:24:32 +02:00
parent 993113b2b7
commit 005b3f4530
13 changed files with 114 additions and 64 deletions

View file

@ -90,8 +90,11 @@ class PortalSessionHandler(SessionHandler):
if session:
# assign if we are first-connectors
self.latest_sessid += 1
session.sessid = self.latest_sessid
if not session.sessid:
# if the session already has a sessid (e.g. being inherited in the
# case of a webclient auto-reconnect), keep it
self.latest_sessid += 1
session.sessid = self.latest_sessid
session.server_connected = False
_CONNECTION_QUEUE.appendleft(session)
if len(_CONNECTION_QUEUE) > 1:

View file

@ -29,6 +29,9 @@ _RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, r
_CLIENT_SESSIONS = mod_import(settings.SESSION_ENGINE).SessionStore
CLOSE_NORMAL = WebSocketServerProtocol.CLOSE_STATUS_CODE_NORMAL
class WebSocketClient(WebSocketServerProtocol, Session):
"""
Implements the server-side of the Websocket connection.
@ -70,19 +73,21 @@ class WebSocketClient(WebSocketServerProtocol, Session):
client_address = client_address[0] if client_address else None
self.init_session("websocket", client_address, self.factory.sessionhandler)
from evennia.utils import logger
try:
csessid = self.http_request_uri.split("?", 1)[1]
except Exception:
logger.log_trace(str(self.__dict__))
csession = self.get_client_session()
csession = self.get_client_session() # this sets self.csessid
csessid = self.csessid
uid = csession and csession.get("webclient_authenticated_uid", None)
if uid:
# the client session is already logged in.
self.uid = uid
self.logged_in = True
for old_session in self.sessionhandler.sessions_from_csessid(csessid):
if (hasattr(old_session, "websocket_close_code") and
old_session.websocket_close_code != CLOSE_NORMAL):
# if we have old sessions with the same csession, they are remnants
self.sessid = old_session.sessid
self.sessionhandler.disconnect(old_session)
# watch for dead links
self.transport.setTcpKeepAlive(1)
# actually do the connection
@ -97,11 +102,19 @@ class WebSocketClient(WebSocketServerProtocol, Session):
reason (str or None): Motivation for the disconnection.
"""
csession = self.get_client_session()
if csession:
csession["webclient_authenticated_uid"] = None
csession.save()
self.logged_in = False
self.sessionhandler.disconnect(self)
# autobahn-python: 1000 for a normal close, 3000-4999 for app. specific,
# in case anyone wants to expose this functionality later.
#
# sendClose() under autobahn/websocket/interfaces.py
self.sendClose(1000, reason)
self.sendClose(CLOSE_NORMAL, reason)
def onClose(self, wasClean, code=None, reason=None):
"""
@ -111,19 +124,14 @@ class WebSocketClient(WebSocketServerProtocol, Session):
Args:
wasClean (bool): ``True`` if the WebSocket was closed cleanly.
reason (str): Motivation for the lost connection.
code (int or None): Close status as sent by the WebSocket peer.
reason (str or None): Close reason as sent by the WebSocket peer.
"""
csession = self.get_client_session()
if csession:
csession["webclient_authenticated_uid"] = None
csession.save()
self.logged_in = False
self.sessionhandler.disconnect(self)
if code == CLOSE_NORMAL:
self.disconnect(reason)
else:
self.websocket_close_code = code
def onMessage(self, payload, isBinary):
"""

View file

@ -214,7 +214,7 @@ class SessionHandler(dict):
return newdict
elif is_iter(data):
return [_validate(part) for part in data]
elif isinstance(data, (str, bytes )):
elif isinstance(data, (str, bytes)):
data = _utf8(data)
if _INLINEFUNC_ENABLED and not raw and isinstance(self, ServerSessionHandler):
@ -257,9 +257,9 @@ class SessionHandler(dict):
return rkwargs
#------------------------------------------------------------
# ------------------------------------------------------------
# Server-SessionHandler class
#------------------------------------------------------------
# ------------------------------------------------------------
class ServerSessionHandler(SessionHandler):
"""
@ -413,7 +413,7 @@ class ServerSessionHandler(SessionHandler):
# set a watchdog to avoid self.disconnect from deleting
# the session while we are looping over them
self._disconnect_all = True
for session in self.values:
for session in self.values():
session.disconnect()
del self._disconnect_all
@ -443,7 +443,8 @@ class ServerSessionHandler(SessionHandler):
"""
self.server.amp_protocol.send_AdminServer2Portal(DUMMYSESSION, operation=SCONN,
protocol_path=protocol_path, config=configdict)
protocol_path=protocol_path,
config=configdict)
def portal_restart_server(self):
"""
@ -544,7 +545,8 @@ class ServerSessionHandler(SessionHandler):
nsess = len(self.sessions_from_account(session.account)) - 1
sreason = " ({})".format(reason) if reason else ""
string = "Logged out: {account} {address} ({nsessions} sessions(s) remaining){reason}"
string = string.format(reason=sreason, account=session.account, address=session.address, nsessions=nsess)
string = string.format(reason=sreason, account=session.account,
address=session.address, nsessions=nsess)
session.log(string)
if nsess == 0:
@ -670,7 +672,8 @@ class ServerSessionHandler(SessionHandler):
amount of Sessions due to multi-playing).
"""
return list(set(session.account for session in self.values() if session.logged_in and session.account))
return list(set(session.account for session in self.values()
if session.logged_in and session.account))
def session_from_sessid(self, sessid):
"""
@ -737,13 +740,17 @@ class ServerSessionHandler(SessionHandler):
def sessions_from_csessid(self, csessid):
"""
Given a cliend identification hash (for session types that offer them) return all sessions with
a matching hash.
Given a client identification hash (for session types that offer them)
return all sessions with a matching hash.
Args
csessid (str): The session hash
csessid (str): The session hash.
Returns:
sessions (list): The sessions with matching .csessid, if any.
"""
if csessid:
return []
return [session for session in self.values()
if session.csessid and session.csessid == csessid]