Made portalsessionhandler manage command rate limitations directly, using a cmd/s average over 200 commands.
This commit is contained in:
parent
e201cda2c3
commit
9793c57dd4
3 changed files with 40 additions and 24 deletions
|
|
@ -1,14 +1,18 @@
|
||||||
"""
|
"""
|
||||||
Sessionhandler for portal sessions
|
Sessionhandler for portal sessions
|
||||||
"""
|
"""
|
||||||
import time
|
from collections import deque
|
||||||
from twisted.internet import reactor
|
from time import time
|
||||||
|
from twisted.internet import reactor, task
|
||||||
from evennia.server.sessionhandler import SessionHandler, PCONN, PDISCONN, PCONNSYNC
|
from evennia.server.sessionhandler import SessionHandler, PCONN, PDISCONN, PCONNSYNC
|
||||||
|
|
||||||
_CONNECTION_RATE = 5.0
|
_CONNECTION_RATE = 5.0
|
||||||
_MIN_TIME_BETWEEN_CONNECTS = 1.0 / _CONNECTION_RATE
|
_MIN_TIME_BETWEEN_CONNECTS = 1.0 / _CONNECTION_RATE
|
||||||
_MOD_IMPORT = None
|
_MOD_IMPORT = None
|
||||||
|
|
||||||
|
_MAX_CMD_RATE = 150.0
|
||||||
|
_ERROR_COMMAND_OVERFLOW = "You entered commands too fast. Wait a moment and try again."
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
# Portal-SessionHandler class
|
# Portal-SessionHandler class
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
@ -31,9 +35,17 @@ class PortalSessionHandler(SessionHandler):
|
||||||
self.portal = None
|
self.portal = None
|
||||||
self.sessions = {}
|
self.sessions = {}
|
||||||
self.latest_sessid = 0
|
self.latest_sessid = 0
|
||||||
self.uptime = time.time()
|
self.uptime = time()
|
||||||
self.connection_time = 0
|
self.connection_time = 0
|
||||||
self.time_last_connect = time.time()
|
self.time_last_connect = time()
|
||||||
|
self.cmd_last = time()
|
||||||
|
self.cmd_rate_history = deque([], 200)
|
||||||
|
self.cmd_rate = 0.0
|
||||||
|
self.overflows = 0
|
||||||
|
task.LoopingCall(self._report, interval=30)
|
||||||
|
|
||||||
|
def _report(self):
|
||||||
|
print " INFO: cmd rate: %s, overflows: %s" % (sum(self.cmd_rate_history) / 200.0, self.overflows)
|
||||||
|
|
||||||
def at_server_connection(self):
|
def at_server_connection(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -41,7 +53,7 @@ class PortalSessionHandler(SessionHandler):
|
||||||
Server. At this point, the AMP connection is already
|
Server. At this point, the AMP connection is already
|
||||||
established.
|
established.
|
||||||
"""
|
"""
|
||||||
self.connection_time = time.time()
|
self.connection_time = time()
|
||||||
|
|
||||||
def connect(self, session):
|
def connect(self, session):
|
||||||
"""
|
"""
|
||||||
|
|
@ -61,7 +73,7 @@ class PortalSessionHandler(SessionHandler):
|
||||||
self.latest_sessid += 1
|
self.latest_sessid += 1
|
||||||
session.sessid = self.latest_sessid
|
session.sessid = self.latest_sessid
|
||||||
|
|
||||||
now = time.time()
|
now = time()
|
||||||
current_rate = 1.0 / (now - self.time_last_connect)
|
current_rate = 1.0 / (now - self.time_last_connect)
|
||||||
|
|
||||||
if current_rate > _CONNECTION_RATE:
|
if current_rate > _CONNECTION_RATE:
|
||||||
|
|
@ -290,6 +302,28 @@ class PortalSessionHandler(SessionHandler):
|
||||||
in from the protocol to the server. data is
|
in from the protocol to the server. data is
|
||||||
serialized before passed on.
|
serialized before passed on.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
now = time()
|
||||||
|
self.cmd_rate_history.append(1.0 / (now - self.cmd_last))
|
||||||
|
|
||||||
|
if session.logged_in:
|
||||||
|
# command flood protection
|
||||||
|
self.cmd_rate = sum(self.cmd_rate_history) / 200.0
|
||||||
|
if self.cmd_rate > _MAX_CMD_RATE:
|
||||||
|
max_rate_per_session = _MAX_CMD_RATE / len(self.sessions)
|
||||||
|
session_rate = 1.0 / (now - session.cmd_last)
|
||||||
|
session.cmd_last = now
|
||||||
|
if session_rate > max_rate_per_session:
|
||||||
|
self.overflows += 1
|
||||||
|
session.data_out(text=_ERROR_COMMAND_OVERFLOW)
|
||||||
|
if 100 % self.overflows == 0:
|
||||||
|
print "CMD OVERFLOW %s: %s (%s/%s, %s/%s)" % (session.sessid, text,
|
||||||
|
self.cmd_rate, _MAX_CMD_RATE,
|
||||||
|
session_rate, max_rate_per_session)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
session.cmd_last = now
|
||||||
|
|
||||||
self.portal.amp_protocol.call_remote_MsgPortal2Server(session.sessid,
|
self.portal.amp_protocol.call_remote_MsgPortal2Server(session.sessid,
|
||||||
msg=text,
|
msg=text,
|
||||||
data=kwargs)
|
data=kwargs)
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,6 @@ class ServerSession(Session):
|
||||||
self.player = None
|
self.player = None
|
||||||
self.cmdset_storage_string = ""
|
self.cmdset_storage_string = ""
|
||||||
self.cmdset = CmdSetHandler(self, True)
|
self.cmdset = CmdSetHandler(self, True)
|
||||||
self.cmd_per_second = 0.0
|
|
||||||
|
|
||||||
def __cmdset_storage_get(self):
|
def __cmdset_storage_get(self):
|
||||||
return [path.strip() for path in self.cmdset_storage_string.split(',')]
|
return [path.strip() for path in self.cmdset_storage_string.split(',')]
|
||||||
|
|
@ -200,10 +199,6 @@ class ServerSession(Session):
|
||||||
oobhandler at this point.
|
oobhandler at this point.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
now = time()
|
|
||||||
self.cmd_per_second = 1.0 / (now - self.cmd_last)
|
|
||||||
self.cmd_last = now
|
|
||||||
|
|
||||||
#explicitly check for None since text can be an empty string, which is
|
#explicitly check for None since text can be an empty string, which is
|
||||||
#also valid
|
#also valid
|
||||||
if text is not None:
|
if text is not None:
|
||||||
|
|
|
||||||
|
|
@ -136,8 +136,6 @@ class ServerSessionHandler(SessionHandler):
|
||||||
self.sessions = {}
|
self.sessions = {}
|
||||||
self.server = None
|
self.server = None
|
||||||
self.server_data = {"servername": _SERVERNAME}
|
self.server_data = {"servername": _SERVERNAME}
|
||||||
self.cmd_last = time()
|
|
||||||
self.cmd_per_second = 0.0
|
|
||||||
|
|
||||||
def portal_connect(self, portalsession):
|
def portal_connect(self, portalsession):
|
||||||
"""
|
"""
|
||||||
|
|
@ -500,17 +498,6 @@ class ServerSessionHandler(SessionHandler):
|
||||||
"""
|
"""
|
||||||
session = self.sessions.get(sessid, None)
|
session = self.sessions.get(sessid, None)
|
||||||
if session:
|
if session:
|
||||||
|
|
||||||
now = time()
|
|
||||||
self.cmd_per_second = 1.0 / (now - self.cmd_last)
|
|
||||||
self.cmd_last = now
|
|
||||||
|
|
||||||
if self.cmd_per_second > _MAX_SERVER_COMMANDS_PER_SECOND:
|
|
||||||
if session.cmd_per_second > _MAX_SESSION_COMMANDS_PER_SECOND:
|
|
||||||
session.data.out(text=_ERROR_COMMAND_OVERFLOW)
|
|
||||||
logger.log_infomsg("overflow kicked in for session %s: %s" % (session.sessid, text))
|
|
||||||
return
|
|
||||||
|
|
||||||
text = text and to_unicode(strip_control_sequences(text), encoding=session.encoding)
|
text = text and to_unicode(strip_control_sequences(text), encoding=session.encoding)
|
||||||
if "oob" in kwargs:
|
if "oob" in kwargs:
|
||||||
# incoming data is always on the form (cmdname, args, kwargs)
|
# incoming data is always on the form (cmdname, args, kwargs)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue