Refactor the MAX_COMMAND_RATE throttle, related to #1556

This commit is contained in:
Griatch 2018-01-21 16:32:00 +01:00
parent 63bf978b39
commit 9e42277fbb

View file

@ -15,12 +15,15 @@ from evennia.utils.logger import log_trace
# module import # module import
_MOD_IMPORT = None _MOD_IMPORT = None
# throttles # global throttles
_MAX_CONNECTION_RATE = float(settings.MAX_CONNECTION_RATE) _MAX_CONNECTION_RATE = float(settings.MAX_CONNECTION_RATE)
# per-session throttles
_MAX_COMMAND_RATE = float(settings.MAX_COMMAND_RATE) _MAX_COMMAND_RATE = float(settings.MAX_COMMAND_RATE)
_MAX_CHAR_LIMIT = int(settings.MAX_CHAR_LIMIT) _MAX_CHAR_LIMIT = int(settings.MAX_CHAR_LIMIT)
_MIN_TIME_BETWEEN_CONNECTS = 1.0 / float(settings.MAX_CONNECTION_RATE) _MIN_TIME_BETWEEN_CONNECTS = 1.0 / float(_MAX_CONNECTION_RATE)
_MIN_TIME_BETWEEN_COMMANDS = 1.0 / float(_MAX_COMMAND_RATE)
_ERROR_COMMAND_OVERFLOW = settings.COMMAND_RATE_WARNING _ERROR_COMMAND_OVERFLOW = settings.COMMAND_RATE_WARNING
_ERROR_MAX_CHAR = settings.MAX_CHAR_LIMIT_WARNING _ERROR_MAX_CHAR = settings.MAX_CHAR_LIMIT_WARNING
@ -58,9 +61,6 @@ class PortalSessionHandler(SessionHandler):
self.connection_last = self.uptime self.connection_last = self.uptime
self.connection_task = None self.connection_task = None
self.command_counter = 0
self.command_counter_reset = self.uptime
self.command_overflow = False
def at_server_connection(self): def at_server_connection(self):
""" """
@ -354,8 +354,6 @@ class PortalSessionHandler(SessionHandler):
Data is serialized before passed on. Data is serialized before passed on.
""" """
# from evennia.server.profiling.timetrace import timetrace # DEBUG
# text = timetrace(text, "portalsessionhandler.data_in") # DEBUG
try: try:
text = kwargs['text'] text = kwargs['text']
if (_MAX_CHAR_LIMIT > 0) and len(text) > _MAX_CHAR_LIMIT: if (_MAX_CHAR_LIMIT > 0) and len(text) > _MAX_CHAR_LIMIT:
@ -367,17 +365,25 @@ class PortalSessionHandler(SessionHandler):
pass pass
if session: if session:
now = time.time() now = time.time()
if self.command_counter > _MAX_COMMAND_RATE > 0:
# data throttle (anti DoS measure) try:
delta_time = now - self.command_counter_reset command_counter_reset = session.command_counter_reset
self.command_counter = 0 except AttributeError:
self.command_counter_reset = now command_counter_reset = session.command_counter_reset = now
self.command_overflow = delta_time < 1.0 session.command_counter = 0
if self.command_overflow:
reactor.callLater(1.0, self.data_in, None) # global command-rate limit
if self.command_overflow: if max(0, now - command_counter_reset) > 1.0:
# more than a second since resetting the counter. Refresh.
session.command_counter_reset = now
session.command_counter = 0
session.command_counter += 1
if session.command_counter * _MIN_TIME_BETWEEN_COMMANDS > 1.0:
self.data_out(session, text=[[_ERROR_COMMAND_OVERFLOW], {}]) self.data_out(session, text=[[_ERROR_COMMAND_OVERFLOW], {}])
return return
if not self.portal.amp_protocol: if not self.portal.amp_protocol:
# this can happen if someone connects before AMP connection # this can happen if someone connects before AMP connection
# was established (usually on first start) # was established (usually on first start)
@ -388,15 +394,9 @@ class PortalSessionHandler(SessionHandler):
kwargs = self.clean_senddata(session, kwargs) kwargs = self.clean_senddata(session, kwargs)
# relay data to Server # relay data to Server
self.command_counter += 1
session.cmd_last = now session.cmd_last = now
self.portal.amp_protocol.send_MsgPortal2Server(session, self.portal.amp_protocol.send_MsgPortal2Server(session,
**kwargs) **kwargs)
else:
# called by the callLater callback
if self.command_overflow:
self.command_overflow = False
reactor.callLater(1.0, self.data_in, None)
def data_out(self, session, **kwargs): def data_out(self, session, **kwargs):
""" """