From f40ca6a7391851fae6a6419530a8b43cc63e8cb5 Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 17 Sep 2020 23:04:41 +0200 Subject: [PATCH 1/4] Try to fix closed-protocol error from dirty-closed webclient --- evennia/server/portal/webclient.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/evennia/server/portal/webclient.py b/evennia/server/portal/webclient.py index 76c8ae8757..d1981fa431 100644 --- a/evennia/server/portal/webclient.py +++ b/evennia/server/portal/webclient.py @@ -24,6 +24,7 @@ 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 +from autobahn.exception import Disconnected _RE_SCREENREADER_REGEX = re.compile( r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE @@ -191,7 +192,12 @@ class WebSocketClient(WebSocketServerProtocol, Session): line (str): Text to send. """ - return self.sendMessage(line.encode()) + try: + return self.sendMessage(line.encode()) + except Disconnected: + # this can happen on an unclean close of certain browsers. + # it means this link is actually already closed. + self.disconnect(reason="Browser already closed.") def at_login(self): csession = self.get_client_session() From 056e000efd76bb6a6cb58d4011293a9ce9b73cf5 Mon Sep 17 00:00:00 2001 From: Griatch Date: Fri, 18 Sep 2020 22:03:38 +0200 Subject: [PATCH 2/4] Make TickerHandler handle malformed input cleaner. Resolve #2191 --- evennia/scripts/tickerhandler.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/evennia/scripts/tickerhandler.py b/evennia/scripts/tickerhandler.py index c17d64d7d5..47dc9f02b3 100644 --- a/evennia/scripts/tickerhandler.py +++ b/evennia/scripts/tickerhandler.py @@ -73,7 +73,7 @@ from evennia.scripts.scripts import ExtendedLoopingCall from evennia.server.models import ServerConfig from evennia.utils.logger import log_trace, log_err from evennia.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj -from evennia.utils import variable_from_module +from evennia.utils import variable_from_module, inherits_from _GA = object.__getattribute__ _SA = object.__setattr__ @@ -319,7 +319,6 @@ class TickerHandler(object): callback (function or method): This is either a stand-alone function or class method on a typeclassed entitye (that is, an entity that can be saved to the database). - Returns: ret (tuple): This is a tuple of the form `(obj, path, callfunc)`, where `obj` is the database object the callback is defined on @@ -327,6 +326,8 @@ class TickerHandler(object): the python-path to the stand-alone function (`None` if a method). The `callfunc` is either the name of the method to call or the callable function object itself. + Raises: + TypeError: If the callback is of an unsupported type. """ outobj, outpath, outcallfunc = None, None, None @@ -337,8 +338,15 @@ class TickerHandler(object): elif inspect.isfunction(callback): outpath = "%s.%s" % (callback.__module__, callback.__name__) outcallfunc = callback + else: + raise TypeError(f"{callback} is not a method or function.") else: - raise TypeError("%s is not a callable function or method." % callback) + raise TypeError(f"{callback} is not a callable function or method.") + + if outobj and not inherits_from(outobj, "evennia.typeclasses.models.TypedObject"): + raise TypeError(f"{callback} is a method on a normal object - it must " + "be either a method on a typeclass, or a stand-alone function.") + return outobj, outpath, outcallfunc def _store_key(self, obj, path, interval, callfunc, idstring="", persistent=True): @@ -504,12 +512,6 @@ class TickerHandler(object): when wanting to modify/remove the ticker later. """ - if isinstance(callback, int): - raise RuntimeError( - "TICKER_HANDLER.add has changed: " - "the interval is now the first argument, callback the second." - ) - obj, path, callfunc = self._get_callback(callback) store_key = self._store_key(obj, path, interval, callfunc, idstring, persistent) kwargs["_obj"] = obj From e36a8e7d2210f6691eb822f427f4649d6fe43d2d Mon Sep 17 00:00:00 2001 From: Griatch Date: Fri, 18 Sep 2020 22:14:09 +0200 Subject: [PATCH 3/4] Handle telnet stumped exception with log --- evennia/server/portal/telnet.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/evennia/server/portal/telnet.py b/evennia/server/portal/telnet.py index 4dbee60f7d..8f43cacd2a 100644 --- a/evennia/server/portal/telnet.py +++ b/evennia/server/portal/telnet.py @@ -81,7 +81,11 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): of incoming data. """ # print(f"telnet dataReceived: {data}") - super().dataReceived(data) + try: + super().dataReceived(data) + except ValueError as err: + from evennia.utils import logger + logger.log_err(f"Malformed telnet input: {err}") def connectionMade(self): """ From a3b1a98bb9e4405f7d550241856536d16839be6b Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 19 Sep 2020 00:19:37 +0200 Subject: [PATCH 4/4] Clean up the webclient/server error output. Resolve #2197 --- evennia/server/portal/webclient.py | 3 ++- evennia/server/webserver.py | 14 ++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/evennia/server/portal/webclient.py b/evennia/server/portal/webclient.py index d1981fa431..155599c7a0 100644 --- a/evennia/server/portal/webclient.py +++ b/evennia/server/portal/webclient.py @@ -40,6 +40,7 @@ CLOSE_NORMAL = WebSocketServerProtocol.CLOSE_STATUS_CODE_NORMAL # called when the browser is navigating away from the page GOING_AWAY = WebSocketServerProtocol.CLOSE_STATUS_CODE_GOING_AWAY +STATE_CLOSING = WebSocketServerProtocol.STATE_CLOSING class WebSocketClient(WebSocketServerProtocol, Session): """ @@ -151,7 +152,7 @@ class WebSocketClient(WebSocketServerProtocol, Session): # in case anyone wants to expose this functionality later. # # sendClose() under autobahn/websocket/interfaces.py - self.sendClose(CLOSE_NORMAL, reason) + ret = self.sendClose(CLOSE_NORMAL, reason) def onClose(self, wasClean, code=None, reason=None): """ diff --git a/evennia/server/webserver.py b/evennia/server/webserver.py index ff7f61baa0..c2b3cad014 100644 --- a/evennia/server/webserver.py +++ b/evennia/server/webserver.py @@ -102,7 +102,8 @@ class EvenniaReverseProxyResource(ReverseProxyResource): """ request.notifyFinish().addErrback( - lambda f: logger.log_trace("%s\nCaught errback in webserver.py:75." % f) + lambda f: 0 + # lambda f: logger.log_trace("%s\nCaught errback in webserver.py" % f) ) return EvenniaReverseProxyResource( self.host, self.port, self.path + "/" + urlquote(path, safe=""), self.reactor @@ -139,9 +140,9 @@ class EvenniaReverseProxyResource(ReverseProxyResource): clientFactory.noisy = False self.reactor.connectTCP(self.host, self.port, clientFactory) # don't trigger traceback if connection is lost before request finish. - request.notifyFinish().addErrback( - lambda f: logger.log_trace("%s\nCaught errback in webserver.py:75." % f) - ) + request.notifyFinish().addErrback(lambda f: 0) + # request.notifyFinish().addErrback( + # lambda f:logger.log_trace("Caught errback in webserver.py: %s" % f) return NOT_DONE_YET @@ -207,6 +208,11 @@ class DjangoWebRoot(resource.Resource): path0 = request.prepath.pop(0) request.postpath.insert(0, path0) + request.notifyFinish().addErrback( + lambda f: 0 + # lambda f: logger.log_trace("%s\nCaught errback in webserver.py:" % f) + ) + deferred = request.notifyFinish() self._pending_requests[deferred] = deferred deferred.addBoth(self._decrement_requests, deferred=deferred)