Esempio n. 1
0
class SlidingRedisBackend(BaseBackend):
    def __init__(
        self,
        host: str = "localhost",
        port: int = 6379,
        db: int = 0,
        password: str = None,
    ) -> None:
        self._redis = StrictRedis(host=host,
                                  port=port,
                                  db=db,
                                  password=password)
        self.sliding_function = self._redis.register_script(
            SLIDING_WINDOW_SCRIPT)

    async def get_limits(self, path: str, user: str, rule: Rule) -> bool:
        epoch = time.time()
        ruleset = rule.ruleset(path, user)
        keys = list(ruleset.keys())
        args = [epoch, json.dumps(ruleset)]
        # quoted_args = [f"'{a}'" for a in args]
        # cli = f"redis-cli --ldb --eval /tmp/script.lua {' '.join(keys)} , {' '.join(quoted_args)}"
        # logger.debug(cli)
        r = await self.sliding_function.execute(keys=keys, args=args)
        # from tests.backends.test_redis import logger
        # logger.debug(f"{epoch} {r} : {all(r)}")
        return all(r)

    async def decrease_limit(self, path: str, user: str, rule: Rule) -> bool:
        raise NotImplementedError()

    async def increase_limit(self, path: str, user: str, rule: Rule) -> bool:
        raise NotImplementedError()

    async def set_block_time(self, user: str, block_time: int) -> None:
        await self._redis.set(f"blocking:{user}", True, block_time)

    async def is_blocking(self, user: str) -> bool:
        return bool(await self._redis.get(f"blocking:{user}"))

    async def allow_request(self, path: str, user: str, rule: Rule) -> bool:
        if await self.is_blocking(user):
            return False

        allow = await self.get_limits(path, user, rule)

        if not allow and rule.block_time:
            await self.set_block_time(user, rule.block_time)

        return allow
Esempio n. 2
0
class RedisBackend(BaseBackend):
    def __init__(
        self,
        host: str = "localhost",
        port: int = 6379,
        db: int = 0,
        password: str = None,
    ) -> None:
        self._redis = StrictRedis(host=host, port=port, db=db, password=password)
        self.decrease_function = self._redis.register_script(DECREASE_SCRIPT)

    async def increase_limit(self, path: str, user: str, rule: Rule) -> bool:
        """
        Return True means successful increase.
        """
        async with await self._redis.pipeline() as pipe:  # type: StrictPipeline
            try:
                timestamp = time.time()
                await pipe.watch(
                    *[f"{path}:{user}:{name}:last_modify" for name in RULENAMES]
                )
                pipe.multi()
                [
                    await pipe.get(f"{path}:{user}:{name}:last_modify")
                    for name in RULENAMES
                ]
                result = [
                    None if _timestamp is None else float(_timestamp)
                    for _timestamp in await pipe.execute()
                ]
                incr_dict = self.calc_incr_value(result, rule)
                if not incr_dict:
                    return False

                pipe.multi()
                for name, data in incr_dict.items():
                    await pipe.set(f"{path}:{user}:{name}", data["value"], data["ttl"])
                    await pipe.set(
                        f"{path}:{user}:{name}:last_modify", timestamp, data["ttl"],
                    )
                await pipe.execute()
                return True
            except WatchError:  # pragma: no cover
                return False

    async def decrease_limit(self, path: str, user: str, rule: Rule) -> bool:
        """
        Return True means successful decrease.
        """
        return await self.decrease_function.execute(
            keys=[
                f"{path}:{user}:{name}"
                for name in RULENAMES
                if getattr(rule, name) is not None
            ]
        )

    async def set_block_time(self, user: str, block_time: int) -> None:
        await self._redis.set(f"blocking:{user}", True, block_time)

    async def is_blocking(self, user: str) -> bool:
        return bool(await self._redis.get(f"blocking:{user}"))