Switch to autobahn-python for WebSockets support.
This commit is contained in:
parent
b80fb95662
commit
bb15fed784
5 changed files with 42 additions and 714 deletions
|
|
@ -295,25 +295,25 @@ if WEBSERVER_ENABLED:
|
|||
|
||||
ajax_webclient = webclient_ajax.AjaxWebClient()
|
||||
ajax_webclient.sessionhandler = PORTAL_SESSIONS
|
||||
web_root.putChild("webclientdata", ajax_webclient)
|
||||
web_root.putChild(b"webclientdata", ajax_webclient)
|
||||
webclientstr = "\n + webclient (ajax only)"
|
||||
|
||||
if WEBSOCKET_CLIENT_ENABLED and not websocket_started:
|
||||
# start websocket client port for the webclient
|
||||
# we only support one websocket client
|
||||
from evennia.server.portal import webclient
|
||||
from evennia.utils.txws import WebSocketFactory
|
||||
from autobahn.twisted.websocket import WebSocketServerFactory
|
||||
|
||||
w_interface = WEBSOCKET_CLIENT_INTERFACE
|
||||
w_ifacestr = ''
|
||||
if w_interface not in ('0.0.0.0', '::') or len(WEBSERVER_INTERFACES) > 1:
|
||||
w_ifacestr = "-%s" % interface
|
||||
port = WEBSOCKET_CLIENT_PORT
|
||||
factory = protocol.ServerFactory()
|
||||
factory = WebSocketServerFactory()
|
||||
factory.noisy = False
|
||||
factory.protocol = webclient.WebSocketClient
|
||||
factory.sessionhandler = PORTAL_SESSIONS
|
||||
websocket_service = internet.TCPServer(port, WebSocketFactory(factory), interface=w_interface)
|
||||
websocket_service = internet.TCPServer(port, factory, interface=w_interface)
|
||||
websocket_service.setName('EvenniaWebSocket%s:%s' % (w_ifacestr, proxyport))
|
||||
PORTAL.services.addService(websocket_service)
|
||||
websocket_started = True
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
Webclient based on websockets.
|
||||
|
||||
This implements a webclient with WebSockets (http://en.wikipedia.org/wiki/WebSocket)
|
||||
by use of the txws implementation (https://github.com/MostAwesomeDude/txWS). It is
|
||||
used together with evennia/web/media/javascript/evennia_websocket_webclient.js.
|
||||
by use of the autobahn-python package's implementation (https://github.com/crossbario/autobahn-python).
|
||||
It is used together with evennia/web/media/javascript/evennia_websocket_webclient.js.
|
||||
|
||||
All data coming into the webclient is in the form of valid JSON on the form
|
||||
|
||||
|
|
@ -22,26 +22,17 @@ from evennia.server.session import Session
|
|||
from evennia.utils.utils import to_str, mod_import
|
||||
from evennia.utils.ansi import parse_ansi
|
||||
from evennia.utils.text2html import parse_html
|
||||
from autobahn.twisted.websocket import WebSocketServerProtocol
|
||||
|
||||
_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
|
||||
_CLIENT_SESSIONS = mod_import(settings.SESSION_ENGINE).SessionStore
|
||||
|
||||
|
||||
class WebSocketClient(Protocol, Session):
|
||||
class WebSocketClient(WebSocketServerProtocol, Session):
|
||||
"""
|
||||
Implements the server-side of the Websocket connection.
|
||||
"""
|
||||
|
||||
def connectionMade(self):
|
||||
"""
|
||||
This is called when the connection is first established.
|
||||
|
||||
"""
|
||||
self.transport.validationMade = self.validationMade
|
||||
client_address = self.transport.client
|
||||
client_address = client_address[0] if client_address else None
|
||||
self.init_session("websocket", client_address, self.factory.sessionhandler)
|
||||
|
||||
def get_client_session(self):
|
||||
"""
|
||||
Get the Client browser session (used for auto-login based on browser session)
|
||||
|
|
@ -52,7 +43,7 @@ class WebSocketClient(Protocol, Session):
|
|||
|
||||
"""
|
||||
try:
|
||||
self.csessid = self.transport.location.split("?", 1)[1]
|
||||
self.csessid = self.http_request_uri.split("?", 1)[1]
|
||||
except IndexError:
|
||||
# this may happen for custom webclients not caring for the
|
||||
# browser session.
|
||||
|
|
@ -61,12 +52,15 @@ class WebSocketClient(Protocol, Session):
|
|||
if self.csessid:
|
||||
return _CLIENT_SESSIONS(session_key=self.csessid)
|
||||
|
||||
def validationMade(self):
|
||||
def onOpen(self):
|
||||
"""
|
||||
This is called from the (modified) txws websocket library when
|
||||
the ws handshake and validation has completed fully.
|
||||
This is called when the WebSocket connection is fully established.
|
||||
|
||||
"""
|
||||
client_address = self.transport.client
|
||||
client_address = client_address[0] if client_address else None
|
||||
self.init_session("websocket", client_address, self.factory.sessionhandler)
|
||||
|
||||
csession = self.get_client_session()
|
||||
uid = csession and csession.get("webclient_authenticated_uid", None)
|
||||
if uid:
|
||||
|
|
@ -85,43 +79,48 @@ class WebSocketClient(Protocol, Session):
|
|||
disconnect this protocol.
|
||||
|
||||
Args:
|
||||
reason (str): Motivation for the disconnection.
|
||||
reason (str or None): Motivation for the disconnection.
|
||||
|
||||
"""
|
||||
self.data_out(text=((reason or "",), {}))
|
||||
# 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)
|
||||
|
||||
csession = self.get_client_session()
|
||||
|
||||
if csession:
|
||||
csession["webclient_authenticated_uid"] = None
|
||||
csession.save()
|
||||
self.logged_in = False
|
||||
self.connectionLost(reason)
|
||||
|
||||
def connectionLost(self, reason):
|
||||
def onClose(self, wasClean, code=None, reason=None):
|
||||
"""
|
||||
This is executed when the connection is lost for whatever
|
||||
reason. it can also be called directly, from the disconnect
|
||||
method.
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
print("In connectionLost of webclient")
|
||||
csession = self.get_client_session()
|
||||
|
||||
if csession:
|
||||
csession["webclient_authenticated_uid"] = None
|
||||
csession.save()
|
||||
self.logged_in = False
|
||||
|
||||
self.sessionhandler.disconnect(self)
|
||||
self.transport.close()
|
||||
|
||||
def dataReceived(self, string):
|
||||
def onMessage(self, payload, isBinary):
|
||||
"""
|
||||
Method called when data is coming in over the websocket
|
||||
connection. This is always a JSON object on the following
|
||||
form:
|
||||
[cmdname, [args], {kwargs}]
|
||||
Callback fired when a complete WebSocket message was received.
|
||||
|
||||
Args:
|
||||
payload (bytes): The WebSocket message received.
|
||||
isBinary (bool): Flag indicating whether payload is binary or
|
||||
UTF-8 encoded text.
|
||||
|
||||
"""
|
||||
cmdarray = json.loads(string)
|
||||
cmdarray = json.loads(payload)
|
||||
if cmdarray:
|
||||
self.data_in(**{cmdarray[0]: [cmdarray[1], cmdarray[2]]})
|
||||
|
||||
|
|
@ -133,7 +132,7 @@ class WebSocketClient(Protocol, Session):
|
|||
line (str): Text to send.
|
||||
|
||||
"""
|
||||
return self.transport.write(line)
|
||||
return self.sendMessage(line.encode())
|
||||
|
||||
def at_login(self):
|
||||
csession = self.get_client_session()
|
||||
|
|
@ -162,22 +161,12 @@ class WebSocketClient(Protocol, Session):
|
|||
this point.
|
||||
|
||||
"""
|
||||
|
||||
if "websocket_close" in kwargs:
|
||||
self.disconnect()
|
||||
return
|
||||
|
||||
self.sessionhandler.data_in(self, **kwargs)
|
||||
|
||||
def data_out(self, **kwargs):
|
||||
"""
|
||||
Data Evennia->User.
|
||||
|
||||
Kwargs:
|
||||
kwargs (any): Options ot the protocol
|
||||
"""
|
||||
self.sessionhandler.data_out(self, **kwargs)
|
||||
|
||||
def send_text(self, *args, **kwargs):
|
||||
"""
|
||||
Send text data. This will pre-process the text for
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue