From fa5c7301fc7062c916a14a8bd35f6c85d4e25bc3 Mon Sep 17 00:00:00 2001 From: A Rodian Jedi Date: Sun, 6 Oct 2024 17:39:08 -0600 Subject: [PATCH] fixing memory leaks --- evennia/server/portal/mccp.py | 23 ++++++------ evennia/server/portal/mssp.py | 15 ++++---- evennia/server/portal/mxp.py | 19 +++++----- evennia/server/portal/naws.py | 21 +++++------ evennia/server/portal/suppress_ga.py | 18 +++++----- evennia/server/portal/telnet.py | 2 ++ evennia/server/portal/telnet_oob.py | 33 +++++++++--------- evennia/server/portal/ttype.py | 52 +++++++++++++++------------- 8 files changed, 97 insertions(+), 86 deletions(-) diff --git a/evennia/server/portal/mccp.py b/evennia/server/portal/mccp.py index ba748e0010..8f7aa00de5 100644 --- a/evennia/server/portal/mccp.py +++ b/evennia/server/portal/mccp.py @@ -15,6 +15,7 @@ This protocol is implemented by the telnet protocol importing mccp_compress and calling it from its write methods. """ +import weakref import zlib # negotiations for v1 and v2 of the protocol @@ -57,10 +58,10 @@ class Mccp: """ - self.protocol = protocol - self.protocol.protocol_flags["MCCP"] = False + self.protocol = weakref.ref(protocol) + self.protocol().protocol_flags["MCCP"] = False # ask if client will mccp, connect callbacks to handle answer - self.protocol.will(MCCP).addCallbacks(self.do_mccp, self.no_mccp) + self.protocol().will(MCCP).addCallbacks(self.do_mccp, self.no_mccp) def no_mccp(self, option): """ @@ -70,10 +71,10 @@ class Mccp: option (Option): Option dict (not used). """ - if hasattr(self.protocol, "zlib"): - del self.protocol.zlib - self.protocol.protocol_flags["MCCP"] = False - self.protocol.handshake_done() + if hasattr(self.protocol(), "zlib"): + del self.protocol().zlib + self.protocol().protocol_flags["MCCP"] = False + self.protocol().handshake_done() def do_mccp(self, option): """ @@ -84,7 +85,7 @@ class Mccp: option (Option): Option dict (not used). """ - self.protocol.protocol_flags["MCCP"] = True - self.protocol.requestNegotiation(MCCP, b"") - self.protocol.zlib = zlib.compressobj(9) - self.protocol.handshake_done() + self.protocol().protocol_flags["MCCP"] = True + self.protocol().requestNegotiation(MCCP, b"") + self.protocol().zlib = zlib.compressobj(9) + self.protocol().handshake_done() diff --git a/evennia/server/portal/mssp.py b/evennia/server/portal/mssp.py index 03714cf9da..2b0a3bbe9f 100644 --- a/evennia/server/portal/mssp.py +++ b/evennia/server/portal/mssp.py @@ -11,6 +11,7 @@ active players and so on. """ +import weakref from django.conf import settings from evennia.utils import utils @@ -39,8 +40,8 @@ class Mssp: protocol (Protocol): The active protocol instance. """ - self.protocol = protocol - self.protocol.will(MSSP).addCallbacks(self.do_mssp, self.no_mssp) + self.protocol = weakref.ref(protocol) + self.protocol().will(MSSP).addCallbacks(self.do_mssp, self.no_mssp) def get_player_count(self): """ @@ -50,7 +51,7 @@ class Mssp: count (int): The number of players in the MUD. """ - return str(self.protocol.sessionhandler.count_loggedin()) + return str(self.protocol().sessionhandler.count_loggedin()) def get_uptime(self): """ @@ -60,7 +61,7 @@ class Mssp: uptime (int): Number of seconds of uptime. """ - return str(self.protocol.sessionhandler.uptime) + return str(self.protocol().sessionhandler.uptime) def no_mssp(self, option): """ @@ -71,7 +72,7 @@ class Mssp: option (Option): Not used. """ - self.protocol.handshake_done() + self.protocol().handshake_done() def do_mssp(self, option): """ @@ -132,5 +133,5 @@ class Mssp: ) # send to crawler by subnegotiation - self.protocol.requestNegotiation(MSSP, varlist) - self.protocol.handshake_done() + self.protocol().requestNegotiation(MSSP, varlist) + self.protocol().handshake_done() diff --git a/evennia/server/portal/mxp.py b/evennia/server/portal/mxp.py index a445168c97..b9952864c3 100644 --- a/evennia/server/portal/mxp.py +++ b/evennia/server/portal/mxp.py @@ -15,6 +15,7 @@ http://www.gammon.com.au/mushclient/addingservermxp.htm """ import re +import weakref from django.conf import settings @@ -61,10 +62,10 @@ class Mxp: protocol (Protocol): The active protocol instance. """ - self.protocol = protocol - self.protocol.protocol_flags["MXP"] = False + self.protocol = weakref.ref(protocol) + self.protocol().protocol_flags["MXP"] = False if settings.MXP_ENABLED: - self.protocol.will(MXP).addCallbacks(self.do_mxp, self.no_mxp) + self.protocol().will(MXP).addCallbacks(self.do_mxp, self.no_mxp) def no_mxp(self, option): """ @@ -74,8 +75,8 @@ class Mxp: option (Option): Not used. """ - self.protocol.protocol_flags["MXP"] = False - self.protocol.handshake_done() + self.protocol().protocol_flags["MXP"] = False + self.protocol().handshake_done() def do_mxp(self, option): """ @@ -86,8 +87,8 @@ class Mxp: """ if settings.MXP_ENABLED: - self.protocol.protocol_flags["MXP"] = True - self.protocol.requestNegotiation(MXP, b"") + self.protocol().protocol_flags["MXP"] = True + self.protocol().requestNegotiation(MXP, b"") else: - self.protocol.wont(MXP) - self.protocol.handshake_done() + self.protocol().wont(MXP) + self.protocol().handshake_done() diff --git a/evennia/server/portal/naws.py b/evennia/server/portal/naws.py index 71d3a75352..991e61c04e 100644 --- a/evennia/server/portal/naws.py +++ b/evennia/server/portal/naws.py @@ -11,6 +11,7 @@ client and update it when the size changes """ from codecs import encode as codecs_encode +import weakref from django.conf import settings @@ -41,13 +42,13 @@ class Naws: """ self.naws_step = 0 - self.protocol = protocol - self.protocol.protocol_flags["SCREENWIDTH"] = { + self.protocol = weakref.ref(protocol) + self.protocol().protocol_flags["SCREENWIDTH"] = { 0: DEFAULT_WIDTH } # windowID (0 is root):width self.protocol.protocol_flags["SCREENHEIGHT"] = {0: DEFAULT_HEIGHT} # windowID:width - self.protocol.negotiationMap[NAWS] = self.negotiate_sizes - self.protocol.do(NAWS).addCallbacks(self.do_naws, self.no_naws) + self.protocol().negotiationMap[NAWS] = self.negotiate_sizes + self.protocol().do(NAWS).addCallbacks(self.do_naws, self.no_naws) def no_naws(self, option): """ @@ -58,8 +59,8 @@ class Naws: option (Option): Not used. """ - self.protocol.protocol_flags["AUTORESIZE"] = False - self.protocol.handshake_done() + self.protocol().protocol_flags["AUTORESIZE"] = False + self.protocol().handshake_done() def do_naws(self, option): """ @@ -69,8 +70,8 @@ class Naws: option (Option): Not used. """ - self.protocol.protocol_flags["AUTORESIZE"] = True - self.protocol.handshake_done() + self.protocol().protocol_flags["AUTORESIZE"] = True + self.protocol().handshake_done() def negotiate_sizes(self, options): """ @@ -83,6 +84,6 @@ class Naws: if len(options) == 4: # NAWS is negotiated with 16bit words width = options[0] + options[1] - self.protocol.protocol_flags["SCREENWIDTH"][0] = int(codecs_encode(width, "hex"), 16) + self.protocol().protocol_flags["SCREENWIDTH"][0] = int(codecs_encode(width, "hex"), 16) height = options[2] + options[3] - self.protocol.protocol_flags["SCREENHEIGHT"][0] = int(codecs_encode(height, "hex"), 16) + self.protocol().protocol_flags["SCREENHEIGHT"][0] = int(codecs_encode(height, "hex"), 16) diff --git a/evennia/server/portal/suppress_ga.py b/evennia/server/portal/suppress_ga.py index cadf007fa9..f933b359e5 100644 --- a/evennia/server/portal/suppress_ga.py +++ b/evennia/server/portal/suppress_ga.py @@ -14,6 +14,8 @@ http://www.faqs.org/rfcs/rfc858.html """ +import weakref + SUPPRESS_GA = bytes([3]) # b"\x03" # default taken from telnet specification @@ -36,14 +38,14 @@ class SuppressGA: protocol (Protocol): The active protocol instance. """ - self.protocol = protocol + self.protocol = weakref.ref(protocol) - self.protocol.protocol_flags["NOGOAHEAD"] = True - self.protocol.protocol_flags["NOPROMPTGOAHEAD"] = ( + self.protocol().protocol_flags["NOGOAHEAD"] = True + self.protocol().protocol_flags["NOPROMPTGOAHEAD"] = ( True # Used to send a GA after a prompt line only, set in TTYPE (per client) ) # tell the client that we prefer to suppress GA ... - self.protocol.will(SUPPRESS_GA).addCallbacks(self.will_suppress_ga, self.wont_suppress_ga) + self.protocol().will(SUPPRESS_GA).addCallbacks(self.will_suppress_ga, self.wont_suppress_ga) def wont_suppress_ga(self, option): """ @@ -53,8 +55,8 @@ class SuppressGA: option (Option): Not used. """ - self.protocol.protocol_flags["NOGOAHEAD"] = False - self.protocol.handshake_done() + self.protocol().protocol_flags["NOGOAHEAD"] = False + self.protocol().handshake_done() def will_suppress_ga(self, option): """ @@ -64,5 +66,5 @@ class SuppressGA: option (Option): Not used. """ - self.protocol.protocol_flags["NOGOAHEAD"] = True - self.protocol.handshake_done() + self.protocol().protocol_flags["NOGOAHEAD"] = True + self.protocol().handshake_done() diff --git a/evennia/server/portal/telnet.py b/evennia/server/portal/telnet.py index 46d1de0baa..e561953f18 100644 --- a/evennia/server/portal/telnet.py +++ b/evennia/server/portal/telnet.py @@ -306,6 +306,8 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, _BASE_SESSION_CLASS): """ self.sessionhandler.disconnect(self) + if self.nop_keep_alive and self.nop_keep_alive.running: + self.toggle_nop_keepalive() self.transport.loseConnection() def applicationDataReceived(self, data): diff --git a/evennia/server/portal/telnet_oob.py b/evennia/server/portal/telnet_oob.py index ffc2283067..c8c3365ab0 100644 --- a/evennia/server/portal/telnet_oob.py +++ b/evennia/server/portal/telnet_oob.py @@ -26,6 +26,7 @@ This implements the following telnet OOB communication protocols: import json import re +import weakref # General Telnet from twisted.conch.telnet import IAC, SB, SE @@ -84,16 +85,16 @@ class TelnetOOB: protocol (Protocol): The active protocol. """ - self.protocol = protocol - self.protocol.protocol_flags["OOB"] = False + self.protocol = weakref.ref(protocol) + self.protocol().protocol_flags["OOB"] = False self.MSDP = False self.GMCP = False # ask for the available protocols and assign decoders # (note that handshake_done() will be called twice!) - self.protocol.negotiationMap[MSDP] = self.decode_msdp - self.protocol.negotiationMap[GMCP] = self.decode_gmcp - self.protocol.will(MSDP).addCallbacks(self.do_msdp, self.no_msdp) - self.protocol.will(GMCP).addCallbacks(self.do_gmcp, self.no_gmcp) + self.protocol().negotiationMap[MSDP] = self.decode_msdp + self.protocol().negotiationMap[GMCP] = self.decode_gmcp + self.protocol().will(MSDP).addCallbacks(self.do_msdp, self.no_msdp) + self.protocol().will(GMCP).addCallbacks(self.do_gmcp, self.no_gmcp) self.oob_reported = {} def no_msdp(self, option): @@ -105,7 +106,7 @@ class TelnetOOB: """ # no msdp, check GMCP - self.protocol.handshake_done() + self.protocol().handshake_done() def do_msdp(self, option): """ @@ -116,8 +117,8 @@ class TelnetOOB: """ self.MSDP = True - self.protocol.protocol_flags["OOB"] = True - self.protocol.handshake_done() + self.protocol().protocol_flags["OOB"] = True + self.protocol().handshake_done() def no_gmcp(self, option): """ @@ -128,7 +129,7 @@ class TelnetOOB: option (Option): Not used. """ - self.protocol.handshake_done() + self.protocol().handshake_done() def do_gmcp(self, option): """ @@ -139,8 +140,8 @@ class TelnetOOB: """ self.GMCP = True - self.protocol.protocol_flags["OOB"] = True - self.protocol.handshake_done() + self.protocol().protocol_flags["OOB"] = True + self.protocol().handshake_done() # encoders @@ -375,7 +376,7 @@ class TelnetOOB: cmds["msdp_{}".format(remap)] = cmds.pop(lower_case[remap]) # print("msdp data in:", cmds) # DEBUG - self.protocol.data_in(**cmds) + self.protocol().data_in(**cmds) def decode_gmcp(self, data): """ @@ -424,7 +425,7 @@ class TelnetOOB: if cmdname.lower().startswith(b"core_"): # if Core.cmdname, then use cmdname cmdname = cmdname[5:] - self.protocol.data_in(**{cmdname.lower().decode(): [args, kwargs]}) + self.protocol().data_in(**{cmdname.lower().decode(): [args, kwargs]}) # access methods @@ -441,8 +442,8 @@ class TelnetOOB: if self.MSDP: encoded_oob = self.encode_msdp(cmdname, *args, **kwargs) - self.protocol._write(IAC + SB + MSDP + encoded_oob + IAC + SE) + self.protocol()._write(IAC + SB + MSDP + encoded_oob + IAC + SE) if self.GMCP: encoded_oob = self.encode_gmcp(cmdname, *args, **kwargs) - self.protocol._write(IAC + SB + GMCP + encoded_oob + IAC + SE) + self.protocol()._write(IAC + SB + GMCP + encoded_oob + IAC + SE) diff --git a/evennia/server/portal/ttype.py b/evennia/server/portal/ttype.py index d1b4c738b2..dbc7d6b8a9 100644 --- a/evennia/server/portal/ttype.py +++ b/evennia/server/portal/ttype.py @@ -12,6 +12,8 @@ under the 'TTYPE' key. """ +import weakref + # telnet option codes TTYPE = bytes([24]) # b"\x18" IS = bytes([0]) # b"\x00" @@ -55,16 +57,16 @@ class Ttype: """ self.ttype_step = 0 - self.protocol = protocol + self.protocol = weakref.ref(protocol) # we set FORCEDENDLINE for clients not supporting ttype - self.protocol.protocol_flags["FORCEDENDLINE"] = True - self.protocol.protocol_flags["TTYPE"] = False + self.protocol().protocol_flags["FORCEDENDLINE"] = True + self.protocol().protocol_flags["TTYPE"] = False # is it a safe bet to assume ANSI is always supported? - self.protocol.protocol_flags["ANSI"] = True + self.protocol().protocol_flags["ANSI"] = True # setup protocol to handle ttype initialization and negotiation - self.protocol.negotiationMap[TTYPE] = self.will_ttype + self.protocol().negotiationMap[TTYPE] = self.will_ttype # ask if client will ttype, connect callback if it does. - self.protocol.do(TTYPE).addCallbacks(self.will_ttype, self.wont_ttype) + self.protocol().do(TTYPE).addCallbacks(self.will_ttype, self.wont_ttype) def wont_ttype(self, option): """ @@ -74,8 +76,8 @@ class Ttype: option (Option): Not used. """ - self.protocol.protocol_flags["TTYPE"] = False - self.protocol.handshake_done() + self.protocol().protocol_flags["TTYPE"] = False + self.protocol().handshake_done() def will_ttype(self, option): """ @@ -104,7 +106,7 @@ class Ttype: if self.ttype_step == 0: # just start the request chain - self.protocol.requestNegotiation(TTYPE, SEND) + self.protocol().requestNegotiation(TTYPE, SEND) elif self.ttype_step == 1: # this is supposed to be the name of the client/terminal. @@ -125,9 +127,9 @@ class Ttype: xterm256 = clientname.split("MUDLET", 1)[1].strip() >= "1.1" # Mudlet likes GA's on a prompt line for the prompt trigger to # match, if it's not wanting NOGOAHEAD. - if not self.protocol.protocol_flags["NOGOAHEAD"]: - self.protocol.protocol_flags["NOGOAHEAD"] = True - self.protocol.protocol_flags["NOPROMPTGOAHEAD"] = False + if not self.protocol().protocol_flags["NOGOAHEAD"]: + self.protocol().protocol_flags["NOGOAHEAD"] = True + self.protocol().protocol_flags["NOPROMPTGOAHEAD"] = False if ( clientname.startswith("XTERM") @@ -153,11 +155,11 @@ class Ttype: truecolor = True # all clients supporting TTYPE at all seem to support ANSI - self.protocol.protocol_flags["ANSI"] = True - self.protocol.protocol_flags["XTERM256"] = xterm256 - self.protocol.protocol_flags["TRUECOLOR"] = truecolor - self.protocol.protocol_flags["CLIENTNAME"] = clientname - self.protocol.requestNegotiation(TTYPE, SEND) + self.protocol().protocol_flags["ANSI"] = True + self.protocol().protocol_flags["XTERM256"] = xterm256 + self.protocol().protocol_flags["TRUECOLOR"] = truecolor + self.protocol().protocol_flags["CLIENTNAME"] = clientname + self.protocol().requestNegotiation(TTYPE, SEND) elif self.ttype_step == 2: # this is a term capabilities flag @@ -170,11 +172,11 @@ class Ttype: and not tupper.endswith("-COLOR") # old Tintin, Putty ) if xterm256: - self.protocol.protocol_flags["ANSI"] = True - self.protocol.protocol_flags["XTERM256"] = xterm256 - self.protocol.protocol_flags["TERM"] = term + self.protocol().protocol_flags["ANSI"] = True + self.protocol().protocol_flags["XTERM256"] = xterm256 + self.protocol().protocol_flags["TERM"] = term # request next information - self.protocol.requestNegotiation(TTYPE, SEND) + self.protocol().requestNegotiation(TTYPE, SEND) elif self.ttype_step == 3: # the MTTS bitstring identifying term capabilities @@ -186,12 +188,12 @@ class Ttype: support = dict( (capability, True) for bitval, capability in MTTS if option & bitval > 0 ) - self.protocol.protocol_flags.update(support) + self.protocol().protocol_flags.update(support) else: # some clients send erroneous MTTS as a string. Add directly. - self.protocol.protocol_flags[option.upper()] = True + self.protocol().protocol_flags[option.upper()] = True - self.protocol.protocol_flags["TTYPE"] = True + self.protocol().protocol_flags["TTYPE"] = True # we must sync ttype once it'd done - self.protocol.handshake_done() + self.protocol().handshake_done() self.ttype_step += 1