Exemple #1
0
async def retrieve_rule(request: web.Request):
    try:
        # we want to identify the parameter which is used to identify the
        # records
        response = []
        if 'status_code' in request.rel_url.query:
            status_code = request.rel_url.query.get('status_code')
            response = await RateLimiter.get_rule_by_status_code(
                status_code, DB.get_redis(request))
        elif 'service_id' in request.rel_url.query:
            service_id = request.rel_url.query.get('service_id')
            response = await RateLimiter.get_rule_by_service_id(
                service_id, DB.get_redis(request))
        elif 'id' in request.rel_url.query:
            _id = request.rel_url.query.get('id')
            Validate.validate_object_id(_id)
            rule = await RateLimiter.get_rule_by_id(_id, DB.get_redis(request))
            if rule:
                response.append(rule)
        else:
            # fallback to get all if no param passed
            response = await RateLimiter.get_all_rules(DB.get_redis(request))
        return web.json_response({
            'data': response,
            'status_code': 200
        },
                                 status=200)
    except Exception as err:
        return Error.handle(err)
Exemple #2
0
async def retrieve_entry(request: web.Request):
    try:
        response = []
        if 'rule_id' in request.rel_url.query:
            rule_id = request.rel_url.query.get('rule_id')
            Validate.validate_object_id(rule_id)
            response = await RateLimiter.get_entry_by_rule_id(
                rule_id, DB.get_redis(request))
        elif 'host' in request.rel_url.query:
            host = request.rel_url.query.get('host')
            response = await RateLimiter.get_entry_by_host(
                host, DB.get_redis(request))
        elif 'id' in request.rel_url.query:
            _id = request.rel_url.query.get('id')
            Validate.validate_object_id(_id)
            response = await RateLimiter.get_entry_by_id(
                _id, DB.get_redis(request))
        else:
            response = await RateLimiter.get_all_entries(DB.get_redis(request))
        return web.json_response({
            'data': response,
            'status_code': 200
        },
                                 status=200)
    except Exception as err:
        return Error.handle(err)
Exemple #3
0
async def handle_circuit_breaker(breaker: object, service: object, request: web.Request, req: object):
    if req['status'] in breaker['status_codes']:
        breaker_count = await CircuitBreaker.get_count(
            str(breaker['_id']),
            DB.get_redis(request)
        )
    if not breaker_count:
        await CircuitBreaker.set_count(
            str(breaker['_id']),
            0,
            breaker['period'],
            DB.get_redis(request)
        )
    else:
        failures_rate = breaker['tripped_count'] / int(breaker_count)
        if failures_rate >= (1 - breaker['threshold']):
            queued = await CircuitBreaker.get_queued(str(breaker['_id']), DB.get_redis(request))
            if not queued or queued is 'False':
                cooldown_eta = datetime.utcnow(
                ) + timedelta(seconds=breaker['cooldown'])
                queue_async_func.s({'func': 'Service.update',
                                    'args': [str(service['_id']),
                                             {'state': ServiceState.DOWN.name},
                                             f'mongo:{service_controller.table}'],
                                    'kwargs': {}}).apply_async()
                queue_async_func.s({
                    'func': 'CircuitBreaker.set_queued',
                    'args': [str(breaker['_id']), 'True', breaker['cooldown'], 'redis'],
                    'kwargs': {}
                }).apply_async()
                queue_async_func.s({'func': 'Service.update',
                                    'args': [str(service['_id']),
                                             {'state': ServiceState.UP.name},
                                             f'mongo:{service_controller.table}'],
                                    'kwargs': {}}).apply_async(eta=cooldown_eta)
                queue_async_func.s({
                    'func': 'CircuitBreaker.set_queued',
                    'args': [str(breaker['_id']), 'False', breaker['cooldown'], 'redis'],
                    'kwargs': {}
                }).apply_async(eta=cooldown_eta)
                queue_async_func.s({'func': 'CircuitBreaker.update',
                                    'args': [str(breaker['_id']),
                                             {'tripped_count': 0},
                                             f'mongo:{circuit_breaker_controller.table}'],
                                    'kwargs': {}}).apply_async(eta=cooldown_eta)
                events = await Event.get_by_circuit_breaker_id(str(breaker['_id']), DB.get(request, event_controller.table))
                for event in events:
                    queue_async_func.s({
                        'func': 'Event.handle_event',
                        'args': [Bson.to_json(event)],
                        'kwargs': {}
                    }).apply_async()
    await Async.all([
        CircuitBreaker.incr_tripped_count(str(breaker['_id']), DB.get(
            request, circuit_breaker_controller.table)),
        CircuitBreaker.incr_count(str(breaker['_id']), DB.get_redis(request))
    ])
Exemple #4
0
async def handle_request(request: web.Request, service: object, endpoint_cacher: object):
    req_ctx = {
        'method': request.method,
        'url': service['targets'][service['cur_target_index']],
        'params': dict(request.rel_url.query),
        'data': await request.text(),
        'cookies': dict(request.cookies),
        'headers': pydash.omit(dict(request.headers), 'Host'),
    }
    req = None
    req_cache = None
    req_ctx_hash = None

    if not pydash.is_empty(endpoint_cacher):
        req_ctx_hash = Hasher.hash_sha_256(json.dumps(req_ctx))
        req_cache = await EndpointCacher.get_cache(req_ctx_hash, DB.get_redis(request))

    if pydash.is_empty(req_cache):
        req = await Api.call(**req_ctx)
        if pydash.is_empty(req_ctx_hash):
            req_ctx_hash = Hasher.hash_sha_256(json.dumps(req_ctx))
        not pydash.is_empty(endpoint_cacher) and queue_async_func.s({
            'func': 'EndpointCacher.set_cache',
            'args': [req_ctx_hash, req, int(endpoint_cacher['timeout']), 'redis'],
            'kwargs': {}
        }).apply_async()
    else:
        req = json.loads(req_cache)

    cache_hit = True if not pydash.is_empty(req_cache) else False
    return req, cache_hit
Exemple #5
0
async def handle_rate_limiter(request: web.Request, service_id: str, rule: object):
    if not pydash.is_empty(rule):
        entries = await RateLimiter.get_entry_by_rule_id(rule['_id'], DB.get_redis(request))
        if not pydash.is_empty(entries):
            entry = entries[0]
            if int(entry['count']) >= int(rule['max_requests']):
                raise Exception({
                    'message': rule['message'] or 'Too Many Requests',
                    'status_code': int(rule['status_code']) or 429
                })
            queue_async_func.s({
                'func': 'RateLimiter.increment_entry_count',
                'args': [entry['_id'], 'redis'],
                'kwargs': {}
            }).apply_async()
        else:
            entry = {
                'rule_id': rule['_id'],
                'host': request.remote,
                'count': 1,
                'timeout': int(rule['timeout'])
            }
            queue_async_func.s({
                'func': 'RateLimiter.create_entry',
                'args': [entry, 'redis'],
                'kwargs': {}
            }).apply_async()
Exemple #6
0
async def proxy(request: web.Request, handler: web.RequestHandler):
    try:
        req_start_time = time()
        if pydash.starts_with(request.path_qs, '/raven'):
            return await handler(request)

        service = Regex.best_match(await Regex.get_matched_paths(request.path, DB.get(request, service_controller.table)))
        await handle_service(service, request.remote)

        rate_limiter_rules = await RateLimiter.get_rule_by_service_id(str(service['_id']), DB.get_redis(request))
        rate_limiter_rule = rate_limiter_rules[0] if rate_limiter_rules else None
        await handle_rate_limiter(request, str(service['_id']), rate_limiter_rule)

        breakers = await CircuitBreaker.get_by_service_id(str(service['_id']), DB.get(request, circuit_breaker_controller.table))
        breaker = breakers[0] if breakers else None

        request_validators = await RequestValidator.get_by_service_id(str(service['_id']), DB.get(request, request_validator_controller.table))
        request_validator = request_validators[0] if request_validators else None

        endpoint_cachers = not pydash.is_empty(service) and await EndpointCacher.get_by_service_id(str(service['_id']), DB.get_redis(request)) or None
        endpoint_cacher = endpoint_cachers[0] if endpoint_cachers else None

        await handle_request_validator(request_validator, json.loads(await request.text()), request.method)
        req, req_cache_hit = await handle_request(request, service, endpoint_cacher)

        checks = []

        if not pydash.is_empty(
                breaker) and breaker['status'] == CircuitBreakerStatus.ON.name:
            if req['status'] in breaker['status_codes']:
                checks.append(handle_circuit_breaker(
                    breaker, service, request, req))
            else:
                await CircuitBreaker.incr_count(str(breaker['_id']), DB.get_redis(request))

        queue_async_func.s({
            'func': 'Service.advance_target',
            'args': [str(service['_id']), f'mongo:{service_controller.table}'],
            'kwargs': {}
        }).apply_async()
        req_finish_time = time()
        req_elapsed_time = req_finish_time - req_start_time
        checks.append(handle_insights(request, req, str(
            service['_id']), req_elapsed_time, req_cache_hit))
        await Async.all(checks)

        return web.Response(
            body=Bytes.decode_bytes(
                req['body_bytes']),
            status=req['status'],
            content_type=req['content_type'],
            headers=CIMultiDict(
                pydash.omit(
                    req['headers'],
                    'Content-Type',
                    'Transfer-Encoding',
                    'Content-Encoding')))
    except Exception as err:
        return Error.handle(err)
Exemple #7
0
async def post_handler(request: web.Request):
    try:
        ctx = json.loads(await request.text())
        Validate.validate_schema(ctx, endpoint_cache_validator)
        await EndpointCacher.create(ctx, DB.get_redis(request), DB.get(request, controller.table))
        return web.json_response({
            'message': 'Endpoint cache created',
            'status_code': 200
        })
    except Exception as err:
        return Error.handle(err)
Exemple #8
0
async def delete_handler(request: web.Request):
    try:
        _id = request.rel_url.query.get('id')
        Validate.validate_object_id(_id)
        await EndpointCacher.delete(_id, DB.get_redis(request))
        return web.json_response({
            'message': 'Endpoint cache deleted',
            'status_code': 200
        })
    except Exception as err:
        return Error.handle(err)
Exemple #9
0
async def create_rule(request: web.Request):
    try:
        ctx = json.loads(await request.text())
        Validate.validate_schema(ctx, rate_limit_rule_validator)
        await RateLimiter.create_rule(ctx, DB.get_redis(request))
        return web.json_response({
            'message': 'Created rate limiter rule',
            'status_code': 200
        })
    except Exception as err:
        return Error.handle(err)
Exemple #10
0
async def get_handler(request: web.Request):
    try:
        response = []
        if 'id' in request.rel_url.query:
            _id = request.rel_url.query.get('id')
            Validate.validate_object_id(_id)
            cache = await EndpointCacher.get_by_id(_id, DB.get_redis(request))
            if not pydash.is_empty(cache):
                response.append(cache)
        elif 'service_id' in request.rel_url.query:
            service_id = request.rel_url.query.get('service_id')
            Validate.validate_object_id(service_id)
            response = await EndpointCacher.get_by_service_id(service_id, DB.get_redis(request))
        else:
            response = await EndpointCacher.get_all(DB.get_redis(request))
        return web.json_response({
            'data': response,
            'status_code': 200
        })
    except Exception as err:
        return Error.handle(err)
Exemple #11
0
async def delete_rule(request: web.Request):
    try:
        # id to delete is from query params
        _id = request.rel_url.query.get('id')
        Validate.validate_object_id(_id)
        await RateLimiter.delete_rule(_id, DB.get_redis(request))
        return web.json_response({
            'message': 'rate limiter rule deleted',
            'status_code': 200
        })
    except Exception as err:
        return Error.handle(err)
Exemple #12
0
async def patch_handler_response_codes(request: web.Request):
    try:
        ctx = json.loads(await request.text())
        _id = request.rel_url.query.get('id')
        action = request.rel_url.query.get('action')
        Validate.validate_object_id(_id)
        Validate.validate_schema(ctx, endpoint_cache_validator)
        if action == 'add':
            await EndpointCacher.add_status_codes(ctx['response_codes'], _id, DB.get_redis(request))
        elif action == 'remove':
            await EndpointCacher.remove_status_codes(ctx['response_codes'], _id, DB.get_redis(request))
        else:
            return web.json_response({
                'message': 'Invalid action provided',
                'status_code': 400,
            }, status=400)
        return web.json_response({
            'message': 'Endpoint cache response codes updated',
            'status_code': 200,
        })
    except Exception as err:
        return Error.handle(err)
Exemple #13
0
async def update_rule(request: web.Request):
    try:
        ctx = json.loads(await request.text())
        _id = request.rel_url.query.get('id')
        Validate.validate_schema(ctx, rate_limit_rule_validator)
        Validate.validate_object_id(_id)
        await RateLimiter.update_rule(_id, ctx, DB.get_redis(request))
        return web.json_response({
            'message': 'rate limiter rule updated',
            'status_code': 200
        })
    except Exception as err:
        return Error.handle(err)
Exemple #14
0
async def patch_handler(request: web.Request):
    try:
        ctx = json.loads(await request.text())
        _id = request.rel_url.query.get('id')
        Validate.validate_schema(ctx, endpoint_cache_validator)
        Validate.validate_object_id(_id)
        await EndpointCacher.update(_id, pydash.omit(ctx, 'service_id', 'response_codes'), DB.get_redis(request))
        return web.json_response({
            'message': 'Endpoint cache updated',
            'status_code': 200
        })
    except Exception as err:
        return Error.handle(err)