def _inner(obj): func = key_func or self._key_func is_route = not isinstance(obj, Blueprint) name = "%s.%s" % ( obj.__module__, obj.__name__ ) if is_route else obj.name dynamic_limit, static_limits = None, [] if callable(limit_value): dynamic_limit = LimitGroup( limit_value, func, _scope, per_method, methods, error_message, exempt_when, override_defaults, deduct_when ) else: try: static_limits = list( LimitGroup( limit_value, func, _scope, per_method, methods, error_message, exempt_when, override_defaults, deduct_when ) ) except ValueError as e: self.logger.error( "failed to configure %s %s (%s)", "view function" if is_route else "blueprint", name, e ) if isinstance(obj, Blueprint): if dynamic_limit: self._blueprint_dynamic_limits.setdefault( name, [] ).append(dynamic_limit) else: self._blueprint_limits.setdefault( name, [] ).extend(static_limits) else: self.__marked_for_limiting.setdefault(name, []).append(obj) if dynamic_limit: self._dynamic_route_limits.setdefault( name, [] ).append(dynamic_limit) else: self._route_limits.setdefault( name, [] ).extend(static_limits) @wraps(obj) def __inner(*a, **k): if ( self._auto_check and not g.get("_rate_limiting_complete") ): self.__check_request_limit(False) g._rate_limiting_complete = True return obj(*a, **k) return __inner
def _inner(obj): func = key_func or self._key_func is_route = not isinstance(obj, Blueprint) name = "%s.%s" % (obj.__module__, obj.__name__) if is_route else obj.name dynamic_limit, static_limits = None, [] if callable(limit_value): dynamic_limit = LimitGroup(limit_value, func, _scope, per_method, methods, error_message, exempt_when) else: try: static_limits = list( LimitGroup(limit_value, func, _scope, per_method, methods, error_message, exempt_when)) except ValueError as e: self.logger.error( "failed to configure %s %s (%s)", "view function" if is_route else "blueprint", name, e) if isinstance(obj, Blueprint): if dynamic_limit: self._blueprint_dynamic_limits.setdefault( name, []).append(dynamic_limit) else: self._blueprint_limits.setdefault(name, []).extend(static_limits) else: @wraps(obj) def __inner(*a, **k): return obj(*a, **k) if dynamic_limit: self._dynamic_route_limits.setdefault( name, []).append(dynamic_limit) else: self._route_limits.setdefault(name, []).extend(static_limits) return __inner
def __configure_fallbacks(self, app, strategy): config = app.config fallback_enabled = config.get(C.IN_MEMORY_FALLBACK_ENABLED, False) fallback_limits = config.get(C.IN_MEMORY_FALLBACK, None) if not self._in_memory_fallback and fallback_limits: self._in_memory_fallback = [ LimitGroup(fallback_limits, self._key_func, None, False, None, None, None, None, None) ] if not self._in_memory_fallback_enabled: self._in_memory_fallback_enabled = ( fallback_enabled or len(self._in_memory_fallback) > 0) if self._in_memory_fallback_enabled: self._fallback_storage = MemoryStorage() self._fallback_limiter = STRATEGIES[strategy]( self._fallback_storage)
def __init__( self, app=None, key_func=None, global_limits=[], default_limits=[], application_limits=[], headers_enabled=False, strategy=None, storage_uri=None, storage_options={}, auto_check=True, swallow_errors=False, in_memory_fallback=[], retry_after=None, key_prefix="", enabled=True ): self.app = app self.logger = logging.getLogger("flask-limiter") self.enabled = enabled self._default_limits = [] self._application_limits = [] self._in_memory_fallback = [] self._exempt_routes = set() self._request_filters = [] self._headers_enabled = headers_enabled self._header_mapping = {} self._retry_after = retry_after self._strategy = strategy self._storage_uri = storage_uri self._storage_options = storage_options self._auto_check = auto_check self._swallow_errors = swallow_errors if not key_func: warnings.warn( "Use of the default `get_ipaddr` function is discouraged." " Please refer to https://flask-limiter.readthedocs.org/#rate-limit-domain" " for the recommended configuration", UserWarning ) if global_limits: self.raise_global_limits_warning() self._key_func = key_func or get_ipaddr self._key_prefix = key_prefix for limit in set(global_limits + default_limits): self._default_limits.extend( [ LimitGroup( limit, self._key_func, None, False, None, None, None ) ] ) for limit in application_limits: self._application_limits.extend( [ LimitGroup( limit, self._key_func, "global", False, None, None, None ) ] ) for limit in in_memory_fallback: self._in_memory_fallback.extend( [ LimitGroup( limit, self._key_func, None, False, None, None, None ) ] ) self._route_limits = {} self._dynamic_route_limits = {} self._blueprint_limits = {} self._blueprint_dynamic_limits = {} self._blueprint_exempt = set() self._storage = self._limiter = None self._storage_dead = False self._fallback_limiter = None self.__check_backend_count = 0 self.__last_check_backend = time.time() self.__marked_for_limiting = {} class BlackHoleHandler(logging.StreamHandler): def emit(*_): return self.logger.addHandler(BlackHoleHandler()) if app: self.init_app(app)
def init_app(self, app): """ :param app: :class:`flask.Flask` instance to rate limit. """ self.enabled = app.config.setdefault(C.ENABLED, self.enabled) self._swallow_errors = app.config.setdefault( C.SWALLOW_ERRORS, self._swallow_errors ) self._headers_enabled = ( self._headers_enabled or app.config.setdefault(C.HEADERS_ENABLED, False) ) self._storage_options.update(app.config.get(C.STORAGE_OPTIONS, {})) self._storage = storage_from_string( self._storage_uri or app.config.setdefault(C.STORAGE_URL, 'memory://'), **self._storage_options ) strategy = ( self._strategy or app.config.setdefault(C.STRATEGY, 'fixed-window') ) if strategy not in STRATEGIES: raise ConfigurationError( "Invalid rate limiting strategy %s" % strategy ) self._limiter = STRATEGIES[strategy](self._storage) self._header_mapping.update( { HEADERS.RESET: self._header_mapping.get(HEADERS.RESET, None) or app.config.setdefault(C.HEADER_RESET, "X-RateLimit-Reset"), HEADERS.REMAINING: self._header_mapping.get(HEADERS.REMAINING, None) or app.config.setdefault( C.HEADER_REMAINING, "X-RateLimit-Remaining" ), HEADERS.LIMIT: self._header_mapping.get(HEADERS.LIMIT, None) or app.config.setdefault(C.HEADER_LIMIT, "X-RateLimit-Limit"), HEADERS.RETRY_AFTER: self._header_mapping.get(HEADERS.RETRY_AFTER, None) or app.config.setdefault(C.HEADER_RETRY_AFTER, "Retry-After"), } ) self._retry_after = ( self._retry_after or app.config.get(C.HEADER_RETRY_AFTER_VALUE) ) self._key_prefix = (self._key_prefix or app.config.get(C.KEY_PREFIX)) app_limits = app.config.get(C.APPLICATION_LIMITS, None) if not self._application_limits and app_limits: self._application_limits = [ LimitGroup( app_limits, self._key_func, "global", False, None, None, None ) ] if app.config.get(C.GLOBAL_LIMITS, None): self.raise_global_limits_warning() conf_limits = app.config.get( C.GLOBAL_LIMITS, app.config.get(C.DEFAULT_LIMITS, None) ) if not self._default_limits and conf_limits: self._default_limits = [ LimitGroup( conf_limits, self._key_func, None, False, None, None, None ) ] fallback_limits = app.config.get(C.IN_MEMORY_FALLBACK, None) if not self._in_memory_fallback and fallback_limits: self._in_memory_fallback = [ LimitGroup( fallback_limits, self._key_func, None, False, None, None, None ) ] if self._in_memory_fallback: self._fallback_storage = MemoryStorage() self._fallback_limiter = STRATEGIES[strategy]( self._fallback_storage ) # purely for backward compatibility as stated in flask documentation if not hasattr(app, 'extensions'): app.extensions = {} # pragma: no cover if not app.extensions.get('limiter'): if self._auto_check: app.before_request(self.__check_request_limit) app.after_request(self.__inject_headers) app.extensions['limiter'] = self
def init_app(self, app): """ :param app: :class:`flask.Flask` instance to rate limit. """ config = app.config self.enabled = config.setdefault(C.ENABLED, self.enabled) if not self.enabled: return self._default_limits_per_method = config.setdefault( C.DEFAULT_LIMITS_PER_METHOD, self._default_limits_per_method ) self._default_limits_exempt_when = config.setdefault( C.DEFAULT_LIMITS_EXEMPT_WHEN, self._default_limits_exempt_when ) self._default_limits_deduct_when = config.setdefault( C.DEFAULT_LIMITS_DEDUCT_WHEN, self._default_limits_deduct_when ) self._swallow_errors = config.setdefault( C.SWALLOW_ERRORS, self._swallow_errors ) self._headers_enabled = ( self._headers_enabled or config.setdefault(C.HEADERS_ENABLED, False) ) self._storage_options.update(config.get(C.STORAGE_OPTIONS, {})) self._storage = storage_from_string( self._storage_uri or config.setdefault(C.STORAGE_URL, 'memory://'), **self._storage_options ) strategy = ( self._strategy or config.setdefault(C.STRATEGY, 'fixed-window') ) if strategy not in STRATEGIES: raise ConfigurationError( "Invalid rate limiting strategy %s" % strategy ) self._limiter = STRATEGIES[strategy](self._storage) # TODO: this should be made consistent with the rest of the # configuration self._header_mapping = { HEADERS.RESET: self._header_mapping.get( HEADERS.RESET, config.get( C.HEADER_RESET, "X-RateLimit-Reset" ) ), HEADERS.REMAINING: self._header_mapping.get( HEADERS.REMAINING, config.get( C.HEADER_REMAINING, "X-RateLimit-Remaining" ) ), HEADERS.LIMIT: self._header_mapping.get( HEADERS.LIMIT, config.get( C.HEADER_LIMIT, "X-RateLimit-Limit" ) ), HEADERS.RETRY_AFTER: self._header_mapping.get( HEADERS.RETRY_AFTER, config.get( C.HEADER_RETRY_AFTER, "Retry-After" ) ), } self._retry_after = ( self._retry_after or config.get(C.HEADER_RETRY_AFTER_VALUE) ) self._key_prefix = (self._key_prefix or config.get(C.KEY_PREFIX)) app_limits = config.get(C.APPLICATION_LIMITS, None) if not self._application_limits and app_limits: self._application_limits = [ LimitGroup( app_limits, self._key_func, "global", False, None, None, None, None, None ) ] if config.get(C.GLOBAL_LIMITS, None): self.__raise_global_limits_warning() conf_limits = config.get( C.GLOBAL_LIMITS, config.get(C.DEFAULT_LIMITS, None) ) if not self._default_limits and conf_limits: self._default_limits = [ LimitGroup( conf_limits, self._key_func, None, False, None, None, None, None, None ) ] for limit in self._default_limits: limit.per_method = self._default_limits_per_method limit.exempt_when = self._default_limits_exempt_when limit.deduct_when = self._default_limits_deduct_when self.__configure_fallbacks(app, strategy) # purely for backward compatibility as stated in flask documentation if not hasattr(app, 'extensions'): app.extensions = {} # pragma: no cover if not app.extensions.get('limiter'): if self._auto_check: app.before_request(self.__check_request_limit) app.after_request(self.__inject_headers) app.extensions['limiter'] = self self.initialized = True