Esempio n. 1
0
 def test_storage_check(self):
     self.assertTrue(storage_from_string("memory://").check())
     self.assertTrue(storage_from_string("redis://localhost:6379").check())
     self.assertTrue(storage_from_string("memcached://localhost:11211").check())
     self.assertTrue(
         storage_from_string("redis+sentinel://localhost:26379", service_name="localhost-redis-sentinel").check()
     )
     self.assertTrue(storage_from_string("redis+cluster://localhost:7000").check())
Esempio n. 2
0
 def test_storage_string(self):
     self.assertTrue(isinstance(storage_from_string("memory://"), MemoryStorage))
     self.assertTrue(isinstance(storage_from_string("redis://localhost:6379"), RedisStorage))
     self.assertTrue(isinstance(storage_from_string("memcached://localhost:11211"), MemcachedStorage))
     self.assertTrue(isinstance(storage_from_string("redis+sentinel://localhost:26379", service_name="localhost-redis-sentinel"), RedisSentinelStorage))
     self.assertTrue(isinstance(storage_from_string("redis+sentinel://localhost:26379/localhost-redis-sentinel"), RedisSentinelStorage))
     self.assertRaises(ConfigurationError, storage_from_string, "blah://")
     self.assertRaises(ConfigurationError, storage_from_string, "redis+sentinel://localhost:26379")
Esempio n. 3
0
    def init_app(self, app):
        """
        :param app: :class:`flask.Flask` instance to rate limit.
        """
        self.enabled = app.config.setdefault(C.ENABLED, True)
        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"),
        })

        conf_limits = app.config.get(C.GLOBAL_LIMITS, None)
        if not self._global_limits and conf_limits:
            self._global_limits = [
                ExtLimit(
                    limit, self._key_func, None, False, None, None, None
                ) for limit in parse_many(conf_limits)
            ]
        fallback_limits = app.config.get(C.IN_MEMORY_FALLBACK, None)
        if not self._in_memory_fallback and fallback_limits:
            self._in_memory_fallback = [
                ExtLimit(
                    limit, self._key_func, None, False, None, None, None
                ) for limit in parse_many(fallback_limits)
                ]
        if self._auto_check:
            app.before_request(self.__check_request_limit)
        app.after_request(self.__inject_headers)

        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
        app.extensions['limiter'] = self
Esempio n. 4
0
 def test_storage_string(self):
     self.assertTrue(isinstance(storage_from_string("memory://"), MemoryStorage))
     self.assertTrue(isinstance(storage_from_string("redis://*****:*****@localhost:26379/localhost-redis-sentinel"),
                 RedisSentinelStorage,
             )
         )
         self.assertEqual(get_dependency().Sentinel.call_args[1]["password"], "foobared")
Esempio n. 5
0
    def test_pluggable_storage_no_moving_window(self):
        class MyStorage(Storage):
            STORAGE_SCHEME = "mystorage"
            def incr(self, key, expiry, elastic_expiry=False):
                return

            def get(self, key):
                return 0

            def get_expiry(self, key):
                return time.time()


        storage = storage_from_string("mystorage://")
        self.assertTrue(isinstance(storage, MyStorage))
        self.assertRaises(NotImplementedError, MovingWindowRateLimiter, storage)
Esempio n. 6
0
 def test_storage_check(self):
     self.assertTrue(storage_from_string("memory://").check())
     self.assertTrue(storage_from_string("redis://localhost:6379").check())
     self.assertTrue(
         storage_from_string(
             "redis+unix:///var/tmp/limits.redis.sock").check())
     self.assertTrue(
         storage_from_string("memcached://localhost:11211").check())
     self.assertTrue(
         storage_from_string(
             "redis+sentinel://localhost:26379",
             service_name="localhost-redis-sentinel").check())
     self.assertTrue(
         storage_from_string("redis+cluster://localhost:7000").check())
     if RUN_GAE:
         self.assertTrue(storage_from_string("gaememcached://").check())
Esempio n. 7
0
    def init_app(self, app):
        """
        :param app: :class:`flask.Flask` instance to rate limit.
        """
        self.enabled = app.config.setdefault(C.ENABLED, True)
        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"),
        })

        conf_limits = app.config.get(C.GLOBAL_LIMITS, None)
        if not self.global_limits and conf_limits:
            self.global_limits = [
                ExtLimit(limit, self.key_func, None, False, None, None)
                for limit in parse_many(conf_limits)
            ]
        if self.auto_check:
            app.before_request(self.__check_request_limit)
        app.after_request(self.__inject_headers)

        # purely for backward compatibility as stated in flask documentation
        if not hasattr(app, 'extensions'):
            app.extensions = {}  # pragma: no cover
        app.extensions['limiter'] = self
Esempio n. 8
0
    def __init__(self):
        conf_limits = getattr(settings, C.GLOBAL_LIMITS, "")
        callback = getattr(settings, C.CALLBACK, self.__raise_exceeded)
        self.enabled = getattr(settings, C.ENABLED, True)
        self.headers_enabled = getattr(settings, C.HEADERS_ENABLED, False)
        self.strategy = getattr(settings, C.STRATEGY, 'fixed-window')
        if self.strategy not in STRATEGIES:
            raise ConfigurationError("Invalid rate limiting strategy %s" %
                                     self.strategy)
        self.storage = storage_from_string(
            getattr(settings, C.STORAGE_URL, "memory://"))
        self.limiter = STRATEGIES[self.strategy](self.storage)
        self.key_function = getattr(settings, C.DEFAULT_KEY_FUNCTION,
                                    get_ipaddr)
        self.global_limits = []
        if conf_limits:
            self.global_limits = [
                LimitWrapper(list(parse_many(conf_limits)), self.key_function,
                             None, False)
            ]
        self.header_mapping = {
            HEADERS.RESET:
            getattr(settings, C.HEADER_RESET, "X-RateLimit-Reset"),
            HEADERS.REMAINING:
            getattr(settings, C.HEADER_REMAINING, "X-RateLimit-Remaining"),
            HEADERS.LIMIT:
            getattr(settings, C.HEADER_LIMIT, "X-RateLimit-Limit"),
        }
        self.logger = logging.getLogger("djlimiter")
        self.logger.addHandler(BlackHoleHandler())

        if isinstance(callback, six.string_types):
            mod, _, name = callback.rpartition(".")
            try:
                self.callback = getattr(importlib.import_module(mod), name)
            except AttributeError:
                self.logger.error(
                    "Unable to load callback function %s. Rate limiting disabled",
                    callback)
                self.enabled = False
        else:
            self.callback = callback
Esempio n. 9
0
    def test_pluggable_storage_moving_window(self):
        class MyStorage(Storage):
            STORAGE_SCHEME = "mystorage"
            def incr(self, key, expiry, elastic_expiry=False):
                return

            def get(self, key):
                return 0

            def get_expiry(self, key):
                return time.time()

            def acquire_entry(self, *a, **k):
                return True

            def get_moving_window(self, *a, **k):
                return (time.time(), 1)

        storage = storage_from_string("mystorage://")
        self.assertTrue(isinstance(storage, MyStorage))
        MovingWindowRateLimiter(storage)
Esempio n. 10
0
    def test_pluggable_storage_moving_window(self):
        class MyStorage(Storage):
            STORAGE_SCHEME = "mystorage"
            def incr(self, key, expiry, elastic_expiry=False):
                return

            def get(self, key):
                return 0

            def get_expiry(self, key):
                return time.time()

            def acquire_entry(self, *a, **k):
                return True

            def get_moving_window(self, *a, **k):
                return (time.time(), 1)

        storage = storage_from_string("mystorage://")
        self.assertTrue(isinstance(storage, MyStorage))
        MovingWindowRateLimiter(storage)
Esempio n. 11
0
 def test_storage_string(self):
     self.assertTrue(
         isinstance(storage_from_string("memory://"), MemoryStorage))
     self.assertTrue(
         isinstance(storage_from_string("redis://*****:*****@localhost:26379/localhost-redis-sentinel"
                 ), RedisSentinelStorage))
         self.assertEqual(
             get_dependency().Sentinel.call_args[1]['password'], 'foobared')
Esempio n. 12
0
    def __init__(self):
        conf_limits = getattr(settings, C.GLOBAL_LIMITS, "")
        callback = getattr(settings, C.CALLBACK, self.__raise_exceeded )
        self.enabled = getattr(settings, C.ENABLED, True)
        self.headers_enabled = getattr(settings, C.HEADERS_ENABLED, False)
        self.strategy = getattr(settings, C.STRATEGY, 'fixed-window')
        if self.strategy not in STRATEGIES:
            raise ConfigurationError("Invalid rate limiting strategy %s" % self.strategy)
        self.storage = storage_from_string(getattr(settings, C.STORAGE_URL, "memory://"))
        self.limiter = STRATEGIES[self.strategy](self.storage)
        self.key_function = getattr(settings, C.DEFAULT_KEY_FUNCTION, get_ipaddr)
        self.global_limits = []
        if conf_limits:
            self.global_limits = [
                LimitWrapper(
                    list(parse_many(conf_limits)), self.key_function, None, False
                )
            ]
        self.header_mapping = {
            HEADERS.RESET : getattr(settings,C.HEADER_RESET, "X-RateLimit-Reset"),
            HEADERS.REMAINING : getattr(settings,C.HEADER_REMAINING, "X-RateLimit-Remaining"),
            HEADERS.LIMIT : getattr(settings,C.HEADER_LIMIT, "X-RateLimit-Limit"),
        }
        self.logger = logging.getLogger("djlimiter")
        self.logger.addHandler(BlackHoleHandler())

        if isinstance(callback, six.string_types):
            mod, _, name = callback.rpartition(".")
            try:
                self.callback = getattr(importlib.import_module(mod), name)
            except AttributeError:
                self.logger.error(
                    "Unable to load callback function %s. Rate limiting disabled",
                    callback
                )
                self.enabled = False
        else:
            self.callback = callback
Esempio n. 13
0
 def test_init_options(self):
     with mock.patch("limits.storage.get_dependency") as get_dependency:
         storage_from_string(self.storage_url, connection_timeout=1)
         self.assertEqual(
             get_dependency().from_url.call_args[1]['connection_timeout'],
             1)
Esempio n. 14
0
    def __init__(
        self,
        # app: Starlette = None,
        key_func: Callable[..., str],
        default_limits: List[StrOrCallableStr] = [],
        application_limits: List[StrOrCallableStr] = [],
        headers_enabled: bool = False,
        strategy: Optional[str] = None,
        storage_uri: Optional[str] = None,
        storage_options: Dict[str, str] = {},
        auto_check: bool = True,
        swallow_errors: bool = False,
        in_memory_fallback: List[StrOrCallableStr] = [],
        in_memory_fallback_enabled: bool = False,
        retry_after: Optional[str] = None,
        key_prefix: str = "",
        enabled: bool = True,
        config_filename: Optional[str] = None,
    ) -> None:
        """
        Configure the rate limiter at app level
        """
        # assert app is not None, "Passing the app instance to the limiter is required"
        # self.app = app
        # app.state.limiter = self

        self.logger = logging.getLogger("slowapi")

        self.app_config = Config(
            config_filename if config_filename is not None else ".env")

        self.enabled = enabled
        self._default_limits = []
        self._application_limits = []
        self._in_memory_fallback: List[LimitGroup] = []
        self._in_memory_fallback_enabled = (in_memory_fallback_enabled
                                            or len(in_memory_fallback) > 0)
        self._exempt_routes: Set[str] = set()
        self._request_filters: List[Callable[..., bool]] = []
        self._headers_enabled = headers_enabled
        self._header_mapping: Dict[int, str] = {}
        self._retry_after: Optional[str] = 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

        self._key_func = key_func
        self._key_prefix = key_prefix

        for limit in set(default_limits):
            self._default_limits.extend([
                LimitGroup(limit, self._key_func, None, False, None, None,
                           None, False)
            ])
        for limit in application_limits:
            self._application_limits.extend([
                LimitGroup(limit, self._key_func, "global", False, None, None,
                           None, False)
            ])
        for limit in in_memory_fallback:
            self._in_memory_fallback.extend([
                LimitGroup(limit, self._key_func, None, False, None, None,
                           None, False)
            ])
        self._route_limits: Dict[str, List[Limit]] = {}
        self._dynamic_route_limits: Dict[str, List[LimitGroup]] = {}
        # a flag to note if the storage backend is dead (not available)
        self._storage_dead: bool = False
        self._fallback_limiter = None
        self.__check_backend_count = 0
        self.__last_check_backend = time.time()
        self.__marked_for_limiting: Dict[str, List[Callable]] = {}

        class BlackHoleHandler(logging.StreamHandler):
            def emit(*_):
                return

        self.logger.addHandler(BlackHoleHandler())

        self.enabled = self.get_app_config(C.ENABLED, self.enabled)
        self._swallow_errors = self.get_app_config(C.SWALLOW_ERRORS,
                                                   self._swallow_errors)
        self._headers_enabled = self._headers_enabled or self.get_app_config(
            C.HEADERS_ENABLED, False)
        self._storage_options.update(self.get_app_config(
            C.STORAGE_OPTIONS, {}))
        self._storage: Storage = storage_from_string(
            self._storage_uri
            or self.get_app_config(C.STORAGE_URL, "memory://"),
            **self._storage_options,
        )
        strategy = self._strategy or self.get_app_config(
            C.STRATEGY, "fixed-window")
        if strategy not in STRATEGIES:
            raise ConfigurationError("Invalid rate limiting strategy %s" %
                                     strategy)
        self._limiter: RateLimiter = STRATEGIES[strategy](self._storage)
        self._header_mapping.update({
            HEADERS.RESET:
            self._header_mapping.get(
                HEADERS.RESET,
                self.get_app_config(C.HEADER_RESET, "X-RateLimit-Reset"),
            ),
            HEADERS.REMAINING:
            self._header_mapping.get(
                HEADERS.REMAINING,
                self.get_app_config(C.HEADER_REMAINING,
                                    "X-RateLimit-Remaining"),
            ),
            HEADERS.LIMIT:
            self._header_mapping.get(
                HEADERS.LIMIT,
                self.get_app_config(C.HEADER_LIMIT, "X-RateLimit-Limit"),
            ),
            HEADERS.RETRY_AFTER:
            self._header_mapping.get(
                HEADERS.RETRY_AFTER,
                self.get_app_config(C.HEADER_RETRY_AFTER, "Retry-After"),
            ),
        })
        self._retry_after = self._retry_after or self.get_app_config(
            C.HEADER_RETRY_AFTER_VALUE)
        self._key_prefix = self._key_prefix or self.get_app_config(
            C.KEY_PREFIX)
        app_limits: Optional[StrOrCallableStr] = self.get_app_config(
            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, False)
            ]

        conf_limits: Optional[StrOrCallableStr] = self.get_app_config(
            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, False)
            ]
        fallback_enabled = self.get_app_config(C.IN_MEMORY_FALLBACK_ENABLED,
                                               False)
        fallback_limits: Optional[StrOrCallableStr] = self.get_app_config(
            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,
                    False,
                )
            ]
        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)
Esempio n. 15
0
def includeme(config):
    config.registry["ratelimiter.storage"] = storage_from_string(
        config.registry.settings["ratelimit.url"]
    )
Esempio n. 16
0
 def test_init_options(self):
     with mock.patch("limits.storage.redis_cluster.get_dependency"
                     ) as get_dependency:
         storage_from_string(self.storage_url, connection_timeout=1)
         call_args = get_dependency().RedisCluster.call_args
         self.assertEqual(call_args[1]['connection_timeout'], 1)
Esempio n. 17
0
    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
Esempio n. 18
0
 def test_options(self):
     with mock.patch(
             "limits.storage.memcached.get_dependency") as get_dependency:
         storage_from_string(self.storage_url, connect_timeout=1).check()
         self.assertEqual(
             get_dependency().Client.call_args[1]['connect_timeout'], 1)
Esempio n. 19
0
 def test_storage_string(self):
     self.assertTrue(isinstance(storage_from_string("memory://"), MemoryStorage))
     self.assertTrue(isinstance(storage_from_string("redis://localhost:6379"), RedisStorage))
     self.assertTrue(isinstance(storage_from_string("memcached://localhost:11211"), MemcachedStorage))
     self.assertRaises(ConfigurationError, storage_from_string, "blah://")
Esempio n. 20
0
 def test_storage_check(self):
     self.assertTrue(storage_from_string("memory://").check())
     self.assertTrue(storage_from_string("redis://localhost:6379").check())
     self.assertTrue(storage_from_string("memcached://localhost:11211").check())
Esempio n. 21
0
 def test_storage_string(self):
     self.assertTrue(isinstance(storage_from_string("memory://"), MemoryStorage))
     self.assertTrue(isinstance(storage_from_string("redis://localhost:6379"), RedisStorage))
     self.assertTrue(isinstance(storage_from_string("memcached://localhost:11211"), MemcachedStorage))
     self.assertRaises(ConfigurationError, storage_from_string, "blah://")
Esempio n. 22
0
    def init_app(self, app):
        """
        :param app: :class:`flask.Flask` instance to rate limit.
        """
        self.enabled = app.config.setdefault(C.ENABLED, True)
        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))

        app_limits = app.config.get(C.APPLICATION_LIMITS, None)
        if not self._application_limits and app_limits:
            self._application_limits = [
                ExtLimit(limit, self._key_func, "global", False, None, None,
                         None) for limit in parse_many(app_limits)
            ]

        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 = [
                ExtLimit(limit, self._key_func, None, False, None, None, None)
                for limit in parse_many(conf_limits)
            ]
        fallback_limits = app.config.get(C.IN_MEMORY_FALLBACK, None)
        if not self._in_memory_fallback and fallback_limits:
            self._in_memory_fallback = [
                ExtLimit(limit, self._key_func, None, False, None, None, None)
                for limit in parse_many(fallback_limits)
            ]
        if self._auto_check:
            app.before_request(self.__check_request_limit)
        app.after_request(self.__inject_headers)

        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
        app.extensions['limiter'] = self
Esempio n. 23
0
def includeme(config):
    config.registry["ratelimiter.storage"] = storage_from_string(
        config.registry.settings["ratelimit.url"])