From c7a7cdf7346c9268b1d4f483b31e1fdc39b6d7e0 Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Tue, 2 Sep 2014 17:57:04 +0100 Subject: Add ratelimiting function to basehandler --- synapse/handlers/_base.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'synapse/handlers') diff --git a/synapse/handlers/_base.py b/synapse/handlers/_base.py index b37c8be964..dc1298366e 100644 --- a/synapse/handlers/_base.py +++ b/synapse/handlers/_base.py @@ -14,6 +14,7 @@ # limitations under the License. from twisted.internet import defer +from synapse.api.errors import cs_error, Codes class BaseHandler(object): @@ -25,8 +26,24 @@ class BaseHandler(object): self.room_lock = hs.get_room_lock_manager() self.state_handler = hs.get_state_handler() self.distributor = hs.get_distributor() + self.ratelimiter = hs.get_ratelimiter() + self.clock = hs.get_clock() self.hs = hs + def ratelimit(self, user_id): + time_now = self.clock.time() + allowed, time_allowed = self.ratelimiter.send_message( + user_id, time_now, + msg_rate_hz=self.hs.config.rc_messages_per_second, + burst_count=self.hs.config.rc_messsage_burst_count, + ) + if not allowed: + raise cs_error( + "Limit exceeded", + Codes.M_LIMIT_EXCEEDED, + retry_after_ms=1000*(time_allowed - time_now), + ) + class BaseRoomHandler(BaseHandler): -- cgit 1.5.1 From 780548b577f5e59cd4f3aa03cf20dbf66a790a39 Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Tue, 2 Sep 2014 18:22:15 +0100 Subject: rate limiting for message sending --- synapse/config/ratelimiting.py | 4 ++-- synapse/handlers/_base.py | 4 ++-- synapse/handlers/message.py | 2 ++ synapse/handlers/room.py | 1 + 4 files changed, 7 insertions(+), 4 deletions(-) (limited to 'synapse/handlers') diff --git a/synapse/config/ratelimiting.py b/synapse/config/ratelimiting.py index 6c41d55d74..a64aeeb6b2 100644 --- a/synapse/config/ratelimiting.py +++ b/synapse/config/ratelimiting.py @@ -12,10 +12,10 @@ class RatelimitConfig(Config): super(RatelimitConfig, cls).add_arguments(parser) rc_group = parser.add_argument_group("ratelimiting") rc_group.add_argument( - "--rc-messages-per-second", type=float, default=0.2 + "--rc-messages-per-second", type=float, default=0.2, help="number of messages a client can send per second" ) rc_group.add_argument( - "--rc_messsage_burst_count", type=float, default=10 + "--rc-message-burst-count", type=float, default=10, help="number of message a client can send before being throttled" ) diff --git a/synapse/handlers/_base.py b/synapse/handlers/_base.py index dc1298366e..c150b60e07 100644 --- a/synapse/handlers/_base.py +++ b/synapse/handlers/_base.py @@ -35,12 +35,12 @@ class BaseHandler(object): allowed, time_allowed = self.ratelimiter.send_message( user_id, time_now, msg_rate_hz=self.hs.config.rc_messages_per_second, - burst_count=self.hs.config.rc_messsage_burst_count, + burst_count=self.hs.config.rc_message_burst_count, ) if not allowed: raise cs_error( "Limit exceeded", - Codes.M_LIMIT_EXCEEDED, + Codes.LIMIT_EXCEEDED, retry_after_ms=1000*(time_allowed - time_now), ) diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index 4aeb2089f5..c9e3c4e451 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -76,6 +76,8 @@ class MessageHandler(BaseRoomHandler): Raises: SynapseError if something went wrong. """ + + self.ratelimit(event.user_id) # TODO(paul): Why does 'event' not have a 'user' object? user = self.hs.parse_userid(event.user_id) assert user.is_mine, "User must be our own: %s" % (user,) diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py index 048b719307..b0c94d35af 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py @@ -49,6 +49,7 @@ class RoomCreationHandler(BaseRoomHandler): SynapseError if the room ID was taken, couldn't be stored, or something went horribly wrong. """ + self.ratelimit(user_id) if "room_alias_name" in config: room_alias = RoomAlias.create_local( -- cgit 1.5.1 From 683596f91e3b074601d607b9e5891ab85b5629ca Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Wed, 3 Sep 2014 08:58:48 +0100 Subject: Raise LimitExceedError when the ratelimiting is throttling requests --- synapse/api/errors.py | 34 +++++++++++++++++++++++++++------- synapse/handlers/_base.py | 6 ++---- 2 files changed, 29 insertions(+), 11 deletions(-) (limited to 'synapse/handlers') diff --git a/synapse/api/errors.py b/synapse/api/errors.py index 3f33ca5b92..23ce0af277 100644 --- a/synapse/api/errors.py +++ b/synapse/api/errors.py @@ -40,10 +40,13 @@ class CodeMessageException(Exception): self.code = code self.msg = msg + def error_dict(self): + return cs_error(self.msg) + class SynapseError(CodeMessageException): """A base error which can be caught for all synapse events.""" - def __init__(self, code, msg, errcode=""): + def __init__(self, code, msg, errcode=Codes.UNKNOWN): """Constructs a synapse error. Args: @@ -54,6 +57,11 @@ class SynapseError(CodeMessageException): super(SynapseError, self).__init__(code, msg) self.errcode = errcode + def error_dict(self): + return cs_error( + self.msg, + self.errcode, + ) class RoomError(SynapseError): """An error raised when a room event fails.""" @@ -92,13 +100,25 @@ class StoreError(SynapseError): pass -def cs_exception(exception): - if isinstance(exception, SynapseError): +class LimitExceededError(SynapseError): + """A client has sent too many requests and is being throttled. + """ + def __init__(self, code=429, msg="Too Many Requests", retry_after_ms=None, + errcode=Codes.LIMIT_EXCEEDED): + super(LimitExceededError, self).__init__(code, msg, errcode) + self.retry_after_ms = retry_after_ms + + def error_dict(self): return cs_error( - exception.msg, - Codes.UNKNOWN if not exception.errcode else exception.errcode) - elif isinstance(exception, CodeMessageException): - return cs_error(exception.msg) + self.msg, + self.errcode, + retry_after_ms=self.retry_after_ms, + ) + + +def cs_exception(exception): + if isinstance(exception, CodeMessageException): + return exception.error_dict() else: logging.error("Unknown exception type: %s", type(exception)) diff --git a/synapse/handlers/_base.py b/synapse/handlers/_base.py index c150b60e07..935adea1ac 100644 --- a/synapse/handlers/_base.py +++ b/synapse/handlers/_base.py @@ -14,7 +14,7 @@ # limitations under the License. from twisted.internet import defer -from synapse.api.errors import cs_error, Codes +from synapse.api.errors import LimitExceededError class BaseHandler(object): @@ -38,9 +38,7 @@ class BaseHandler(object): burst_count=self.hs.config.rc_message_burst_count, ) if not allowed: - raise cs_error( - "Limit exceeded", - Codes.LIMIT_EXCEEDED, + raise LimitExceededError( retry_after_ms=1000*(time_allowed - time_now), ) -- cgit 1.5.1