diff --git a/frontik/app.py b/frontik/app.py index ed2ba196b..a45ebbd97 100644 --- a/frontik/app.py +++ b/frontik/app.py @@ -19,15 +19,15 @@ import frontik.producers.json_producer import frontik.producers.xml_producer -from frontik import integrations, media_types +from frontik import integrations, media_types, request_context from frontik.debug import get_frontik_and_apps_versions from frontik.handler import PageHandler, get_current_handler -from frontik.handler_asgi import execute_page +from frontik.handler_asgi import serve_request from frontik.handler_return_values import ReturnedValueHandlers, get_default_returned_value_handlers from frontik.integrations.statsd import StatsDClient, StatsDClientStub, create_statsd_client from frontik.options import options from frontik.process import WorkerState -from frontik.routing import import_all_pages, router +from frontik.routing import import_all_pages, method_not_allowed_router, not_found_router, router from frontik.service_discovery import UpstreamManager from frontik.util import check_request_id, generate_uniq_timestamp_request_id @@ -102,6 +102,8 @@ def __init__(self) -> None: self.returned_value_handlers: ReturnedValueHandlers = get_default_returned_value_handlers() import_all_pages(self.app_module_name) + assert len(not_found_router.routes) < 2 + assert len(method_not_allowed_router.routes) < 2 self.ui_methods: dict = {} self.ui_modules: dict = {} @@ -114,14 +116,14 @@ def __call__(self, tornado_request: httputil.HTTPServerRequest) -> Optional[Awai request_id = tornado_request.headers.get('X-Request-Id') or generate_uniq_timestamp_request_id() if options.validate_request_id: check_request_id(request_id) + tornado_request.request_id = request_id # type: ignore async def _serve_tornado_request( frontik_app: FrontikApplication, _tornado_request: httputil.HTTPServerRequest, - _request_id: str, asgi_app: FrontikAsgiApp, ) -> None: - status, reason, headers, data = await execute_page(frontik_app, _tornado_request, _request_id, asgi_app) + status, reason, headers, data = await serve_request(frontik_app, _tornado_request, asgi_app) assert _tornado_request.connection is not None _tornado_request.connection.set_close_callback(None) # type: ignore @@ -131,7 +133,8 @@ async def _serve_tornado_request( _tornado_request.connection.finish() return await future - return asyncio.create_task(_serve_tornado_request(self, tornado_request, request_id, self.asgi_app)) + with request_context.request_context(request_id): + return asyncio.create_task(_serve_tornado_request(self, tornado_request, self.asgi_app)) def create_upstream_manager( self, diff --git a/frontik/handler.py b/frontik/handler.py index 1905cd98c..163af1fa6 100644 --- a/frontik/handler.py +++ b/frontik/handler.py @@ -194,7 +194,9 @@ def get_path_argument(self, name: str, default: Any = _ARG_DEFAULT) -> str: if default is _ARG_DEFAULT: raise DefaultValueError(name) return default - value = _remove_control_chars_regex.sub(' ', value) + + if isinstance(value, str): + value = _remove_control_chars_regex.sub(' ', value) return value @overload @@ -400,6 +402,9 @@ async def delete(self, *args, **kwargs): async def head(self, *args, **kwargs): await self._execute_page() + async def options(self, *args, **kwargs): + await self._execute_page() + async def _execute_page(self) -> None: self.stages_logger.commit_stage('prepare') @@ -604,7 +609,10 @@ def send_error(self, status_code: int = 500, **kwargs: Any) -> None: try: self.write_error(status_code, **kwargs) - except Exception: + except Exception as exc: + if isinstance(exc, FinishSignal): + return + self.log.exception('Uncaught exception in write_error') if not self._finished: self.finish() diff --git a/frontik/handler_asgi.py b/frontik/handler_asgi.py index 870445da2..26586d704 100644 --- a/frontik/handler_asgi.py +++ b/frontik/handler_asgi.py @@ -6,14 +6,14 @@ from fastapi.routing import APIRoute from tornado import httputil -from tornado.httputil import HTTPHeaders +from tornado.httputil import HTTPHeaders, HTTPServerRequest from frontik import media_types, request_context from frontik.debug import DebugMode, DebugTransform from frontik.handler import PageHandler, get_default_headers, log_request from frontik.handler_active_limit import request_limiter from frontik.json_builder import JsonBuilder -from frontik.routing import find_route, get_allowed_methods +from frontik.routing import find_route, get_allowed_methods, method_not_allowed_router, not_found_router if TYPE_CHECKING: from frontik.app import FrontikApplication, FrontikAsgiApp @@ -22,54 +22,39 @@ log = logging.getLogger('handler') -async def execute_page( +async def serve_request( frontik_app: FrontikApplication, - tornado_request: httputil.HTTPServerRequest, - request_id: str, + tornado_request: HTTPServerRequest, asgi_app: FrontikAsgiApp, ) -> tuple[int, str, HTTPHeaders, bytes]: - with request_context.request_context(request_id), request_limiter(frontik_app.statsd_client) as accepted: + with request_limiter(frontik_app.statsd_client) as accepted: log.info('requested url: %s', tornado_request.uri) - tornado_request.request_id = request_id # type: ignore + if not accepted: + log_request(tornado_request, http.client.SERVICE_UNAVAILABLE) + return make_not_accepted_response() + + debug_mode = DebugMode(tornado_request) + if debug_mode.auth_failed(): + assert debug_mode.failed_auth_header is not None + log_request(tornado_request, http.client.UNAUTHORIZED) + return make_debug_auth_failed_response(debug_mode.failed_auth_header) + assert tornado_request.method is not None + route, page_cls, path_params = find_route(tornado_request.path, tornado_request.method) + if route is None and tornado_request.method == 'HEAD': + route, page_cls, path_params = find_route(tornado_request.path, 'GET') - debug_mode = DebugMode(tornado_request) data: bytes - if not accepted: - status, reason, headers, data = make_not_accepted_response() - elif debug_mode.auth_failed(): - assert debug_mode.failed_auth_header is not None - status, reason, headers, data = make_debug_auth_failed_response(debug_mode.failed_auth_header) - elif route is None: - status, reason, headers, data = await make_not_found_response(frontik_app, tornado_request) + if route is None: + status, reason, headers, data = await make_not_found_response( + frontik_app, asgi_app, tornado_request, debug_mode + ) else: - request_context.set_handler_name(f'{route.endpoint.__module__}.{route.endpoint.__name__}') - - if page_cls is not None: - status, reason, headers, data = await legacy_process_request( - frontik_app, tornado_request, route, page_cls, path_params, debug_mode - ) - else: - result = {'headers': get_default_headers()} - scope, receive, send = convert_tornado_request_to_asgi( - frontik_app, tornado_request, route, path_params, debug_mode, result - ) - await asgi_app(scope, receive, send) - - status = result['status'] - reason = httputil.responses.get(status, 'Unknown') - headers = HTTPHeaders(result['headers']) - data = result['data'] - - if not scope['json_builder'].is_empty(): - if data != b'null': - raise RuntimeError('Cant have return and json.put at the same time') - - headers['Content-Type'] = media_types.APPLICATION_JSON - data = scope['json_builder'].to_bytes() - headers['Content-Length'] = str(len(data)) + status, reason, headers, data = await execute_page( + frontik_app, asgi_app, tornado_request, route, page_cls, path_params, debug_mode + ) if debug_mode.enabled: debug_transform = DebugTransform(frontik_app, debug_mode) @@ -77,22 +62,79 @@ async def execute_page( reason = httputil.responses.get(status, 'Unknown') log_request(tornado_request, status) - return status, reason, headers, data +async def execute_page( + frontik_app: FrontikApplication, + asgi_app: FrontikAsgiApp, + tornado_request: HTTPServerRequest, + route: APIRoute, + page_cls: type[PageHandler] | None, + path_params: dict, + debug_mode: DebugMode, +) -> tuple[int, str, HTTPHeaders, bytes]: + request_context.set_handler_name(f'{route.endpoint.__module__}.{route.endpoint.__name__}') + + if page_cls is not None: + return await execute_tornado_page(frontik_app, tornado_request, route, page_cls, path_params, debug_mode) + + result: dict = {'headers': get_default_headers()} + scope, receive, send = convert_tornado_request_to_asgi( + frontik_app, tornado_request, route, path_params, debug_mode, result + ) + await asgi_app(scope, receive, send) + + status: int = result['status'] + reason = httputil.responses.get(status, 'Unknown') + headers = HTTPHeaders(result['headers']) + data = result['data'] + + if not scope['json_builder'].is_empty(): + if data != b'null': + raise RuntimeError('Cant have "return" and "json.put" at the same time') + + headers['Content-Type'] = media_types.APPLICATION_JSON + data = scope['json_builder'].to_bytes() + headers['Content-Length'] = str(len(data)) + + return status, reason, headers, data + + async def make_not_found_response( - frontik_app: FrontikApplication, tornado_request: httputil.HTTPServerRequest + frontik_app: FrontikApplication, + asgi_app: FrontikAsgiApp, + tornado_request: httputil.HTTPServerRequest, + debug_mode: DebugMode, ) -> tuple[int, str, HTTPHeaders, bytes]: - allowed_methods = get_allowed_methods(tornado_request.path.strip('/')) + allowed_methods = get_allowed_methods(tornado_request.path) default_headers = get_default_headers() - - if allowed_methods: + headers: Any + + if allowed_methods and len(method_not_allowed_router.routes) != 0: + status, _, headers, data = await execute_page( + frontik_app, + asgi_app, + tornado_request, + method_not_allowed_router.routes[0], # type: ignore + method_not_allowed_router._cls, + {'allowed_methods': allowed_methods}, + debug_mode, + ) + elif allowed_methods: status = 405 headers = {'Allow': ', '.join(allowed_methods)} data = b'' - elif hasattr(frontik_app, 'application_404_handler'): - status, headers, data = await frontik_app.application_404_handler(tornado_request) + elif len(not_found_router.routes) != 0: + status, _, headers, data = await execute_page( + frontik_app, + asgi_app, + tornado_request, + not_found_router.routes[0], # type: ignore + not_found_router._cls, + {}, + debug_mode, + ) else: status, headers, data = build_error_data(404, 'Not Found') @@ -126,7 +168,7 @@ def build_error_data( return status_code, headers, data -async def legacy_process_request( +async def execute_tornado_page( frontik_app: FrontikApplication, tornado_request: httputil.HTTPServerRequest, route: APIRoute, diff --git a/frontik/routing.py b/frontik/routing.py index 7e228ecce..eb999838e 100644 --- a/frontik/routing.py +++ b/frontik/routing.py @@ -17,7 +17,7 @@ routing_logger = logging.getLogger('frontik.routing') routers: list[APIRouter] = [] -_plain_routes: dict[tuple, tuple] = {} +_plain_routes: dict[tuple[str, str], tuple[APIRoute, type[PageHandler] | None]] = {} _regex_mapping: list[tuple[re.Pattern, APIRoute, Type[PageHandler]]] = [] @@ -48,16 +48,22 @@ def head(self, path: str, cls: Optional[Type[PageHandler]] = None, **kwargs: Any self._cls = self._base_cls or cls return super().head(path, **kwargs) - def add_api_route(self, *args, **kwargs): + def options(self, path: str, cls: Optional[Type[PageHandler]] = None, **kwargs: Any) -> Callable: + self._cls = self._base_cls or cls + return super().options(path, **kwargs) + + def add_api_route(self, *args: Any, cls: Optional[Type[PageHandler]] = None, **kwargs: Any) -> None: super().add_api_route(*args, **kwargs) + self._cls = self._base_cls or cls or self._cls route: APIRoute = self.routes[-1] # type: ignore method = next(iter(route.methods), None) + assert method is not None path = route.path.strip('/') if _plain_routes.get((path, method), None) is not None: raise RuntimeError(f'route for {method} {path} already exists') - _plain_routes[(path, method)] = (route, self._cls) # we need our routing, for get route object + _plain_routes[(path, method)] = (route, self._cls) # we need our routing, for getting route object class FrontikRegexRouter(APIRouter): @@ -87,6 +93,10 @@ def head(self, path: str, cls: Optional[Type[PageHandler]] = None, **kwargs: Any self._cls = self._base_cls or cls return super().head(path, **kwargs) + def options(self, path: str, cls: Optional[Type[PageHandler]] = None, **kwargs: Any) -> Callable: + self._cls = self._base_cls or cls + return super().options(path, **kwargs) + def add_api_route(self, *args: Any, cls: Optional[Type[PageHandler]] = None, **kwargs: Any) -> None: super().add_api_route(*args, **kwargs) self._cls = self._base_cls or cls or self._cls @@ -131,6 +141,8 @@ def import_all_pages(app_module: str) -> None: router = FrontikRouter() +not_found_router = FrontikRouter() +method_not_allowed_router = FrontikRouter() regex_router = FrontikRegexRouter() routers.extend((router, regex_router)) @@ -146,7 +158,7 @@ def _find_regex_route( return None, None, None -def find_route(path: str, method: str) -> tuple[APIRoute, type, dict]: +def find_route(path: str, method: str) -> tuple[APIRoute, type[PageHandler], dict]: route: APIRoute route, page_cls, path_params = _find_regex_route(path, method) # type: ignore @@ -164,7 +176,10 @@ def find_route(path: str, method: str) -> tuple[APIRoute, type, dict]: def get_allowed_methods(path: str) -> list[str]: allowed_methods = [] for method in ('GET', 'POST', 'PUT', 'DELETE', 'HEAD'): - route, _ = _plain_routes.get((path, method), (None, None)) + route, _ = _plain_routes.get((path.strip('/'), method), (None, None)) + if route is None: + route, _, _ = _find_regex_route(path, method) + if route is not None: allowed_methods.append(method) diff --git a/poetry.lock b/poetry.lock index b17a9c635..dc83ec957 100644 --- a/poetry.lock +++ b/poetry.lock @@ -502,65 +502,65 @@ grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] [[package]] name = "grpcio" -version = "1.65.1" +version = "1.65.2" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.65.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:3dc5f928815b8972fb83b78d8db5039559f39e004ec93ebac316403fe031a062"}, - {file = "grpcio-1.65.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:8333ca46053c35484c9f2f7e8d8ec98c1383a8675a449163cea31a2076d93de8"}, - {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:7af64838b6e615fff0ec711960ed9b6ee83086edfa8c32670eafb736f169d719"}, - {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbb64b4166362d9326f7efbf75b1c72106c1aa87f13a8c8b56a1224fac152f5c"}, - {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8422dc13ad93ec8caa2612b5032a2b9cd6421c13ed87f54db4a3a2c93afaf77"}, - {file = "grpcio-1.65.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4effc0562b6c65d4add6a873ca132e46ba5e5a46f07c93502c37a9ae7f043857"}, - {file = "grpcio-1.65.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a6c71575a2fedf259724981fd73a18906513d2f306169c46262a5bae956e6364"}, - {file = "grpcio-1.65.1-cp310-cp310-win32.whl", hash = "sha256:34966cf526ef0ea616e008d40d989463e3db157abb213b2f20c6ce0ae7928875"}, - {file = "grpcio-1.65.1-cp310-cp310-win_amd64.whl", hash = "sha256:ca931de5dd6d9eb94ff19a2c9434b23923bce6f767179fef04dfa991f282eaad"}, - {file = "grpcio-1.65.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:bbb46330cc643ecf10bd9bd4ca8e7419a14b6b9dedd05f671c90fb2c813c6037"}, - {file = "grpcio-1.65.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d827a6fb9215b961eb73459ad7977edb9e748b23e3407d21c845d1d8ef6597e5"}, - {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:6e71aed8835f8d9fbcb84babc93a9da95955d1685021cceb7089f4f1e717d719"}, - {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a1c84560b3b2d34695c9ba53ab0264e2802721c530678a8f0a227951f453462"}, - {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27adee2338d697e71143ed147fe286c05810965d5d30ec14dd09c22479bfe48a"}, - {file = "grpcio-1.65.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f62652ddcadc75d0e7aa629e96bb61658f85a993e748333715b4ab667192e4e8"}, - {file = "grpcio-1.65.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:71a05fd814700dd9cb7d9a507f2f6a1ef85866733ccaf557eedacec32d65e4c2"}, - {file = "grpcio-1.65.1-cp311-cp311-win32.whl", hash = "sha256:b590f1ad056294dfaeac0b7e1b71d3d5ace638d8dd1f1147ce4bd13458783ba8"}, - {file = "grpcio-1.65.1-cp311-cp311-win_amd64.whl", hash = "sha256:12e9bdf3b5fd48e5fbe5b3da382ad8f97c08b47969f3cca81dd9b36b86ed39e2"}, - {file = "grpcio-1.65.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:54cb822e177374b318b233e54b6856c692c24cdbd5a3ba5335f18a47396bac8f"}, - {file = "grpcio-1.65.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:aaf3c54419a28d45bd1681372029f40e5bfb58e5265e3882eaf21e4a5f81a119"}, - {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:557de35bdfbe8bafea0a003dbd0f4da6d89223ac6c4c7549d78e20f92ead95d9"}, - {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8bfd95ef3b097f0cc86ade54eafefa1c8ed623aa01a26fbbdcd1a3650494dd11"}, - {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e6a8f3d6c41e6b642870afe6cafbaf7b61c57317f9ec66d0efdaf19db992b90"}, - {file = "grpcio-1.65.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1faaf7355ceed07ceaef0b9dcefa4c98daf1dd8840ed75c2de128c3f4a4d859d"}, - {file = "grpcio-1.65.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:60f1f38eed830488ad2a1b11579ef0f345ff16fffdad1d24d9fbc97ba31804ff"}, - {file = "grpcio-1.65.1-cp312-cp312-win32.whl", hash = "sha256:e75acfa52daf5ea0712e8aa82f0003bba964de7ae22c26d208cbd7bc08500177"}, - {file = "grpcio-1.65.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff5a84907e51924973aa05ed8759210d8cdae7ffcf9e44fd17646cf4a902df59"}, - {file = "grpcio-1.65.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:1fbd6331f18c3acd7e09d17fd840c096f56eaf0ef830fbd50af45ae9dc8dfd83"}, - {file = "grpcio-1.65.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:de5b6be29116e094c5ef9d9e4252e7eb143e3d5f6bd6d50a78075553ab4930b0"}, - {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:e4a3cdba62b2d6aeae6027ae65f350de6dc082b72e6215eccf82628e79efe9ba"}, - {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:941c4869aa229d88706b78187d60d66aca77fe5c32518b79e3c3e03fc26109a2"}, - {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f40cebe5edb518d78b8131e87cb83b3ee688984de38a232024b9b44e74ee53d3"}, - {file = "grpcio-1.65.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2ca684ba331fb249d8a1ce88db5394e70dbcd96e58d8c4b7e0d7b141a453dce9"}, - {file = "grpcio-1.65.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8558f0083ddaf5de64a59c790bffd7568e353914c0c551eae2955f54ee4b857f"}, - {file = "grpcio-1.65.1-cp38-cp38-win32.whl", hash = "sha256:8d8143a3e3966f85dce6c5cc45387ec36552174ba5712c5dc6fcc0898fb324c0"}, - {file = "grpcio-1.65.1-cp38-cp38-win_amd64.whl", hash = "sha256:76e81a86424d6ca1ce7c16b15bdd6a964a42b40544bf796a48da241fdaf61153"}, - {file = "grpcio-1.65.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:cb5175f45c980ff418998723ea1b3869cce3766d2ab4e4916fbd3cedbc9d0ed3"}, - {file = "grpcio-1.65.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b12c1aa7b95abe73b3e04e052c8b362655b41c7798da69f1eaf8d186c7d204df"}, - {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:3019fb50128b21a5e018d89569ffaaaa361680e1346c2f261bb84a91082eb3d3"}, - {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ae15275ed98ea267f64ee9ddedf8ecd5306a5b5bb87972a48bfe24af24153e8"}, - {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f096ffb881f37e8d4f958b63c74bfc400c7cebd7a944b027357cd2fb8d91a57"}, - {file = "grpcio-1.65.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2f56b5a68fdcf17a0a1d524bf177218c3c69b3947cb239ea222c6f1867c3ab68"}, - {file = "grpcio-1.65.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:941596d419b9736ab548aa0feb5bbba922f98872668847bf0720b42d1d227b9e"}, - {file = "grpcio-1.65.1-cp39-cp39-win32.whl", hash = "sha256:5fd7337a823b890215f07d429f4f193d24b80d62a5485cf88ee06648591a0c57"}, - {file = "grpcio-1.65.1-cp39-cp39-win_amd64.whl", hash = "sha256:1bceeec568372cbebf554eae1b436b06c2ff24cfaf04afade729fb9035408c6c"}, - {file = "grpcio-1.65.1.tar.gz", hash = "sha256:3c492301988cd720cd145d84e17318d45af342e29ef93141228f9cd73222368b"}, + {file = "grpcio-1.65.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:51231a22aea830be1d955de5a15da4391b3ac8e1d7868f362c74c15a0e9f5c89"}, + {file = "grpcio-1.65.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:87da0fb85ba42257e450561b0264e36abe47faae07476621ae65d8f5f60f22cd"}, + {file = "grpcio-1.65.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:3a6b36e20b02ca830b15b5eb4abb437de1d42ba93353d1f76b00337108f7ce8e"}, + {file = "grpcio-1.65.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03fdd86ff7d9957b822b9bf1fe0ae1e21e258e9c1d5535a5e9c67de0ad45b6a8"}, + {file = "grpcio-1.65.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e5a67bbf8a1b3be5535802f6e9f507d1d8d38fb32de81ec7f03706d95a9126"}, + {file = "grpcio-1.65.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2ce639f2a2951aedbe9a3636f5730288f9b77c2627f116265d7d2789555e5662"}, + {file = "grpcio-1.65.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b10349ceebec745a47e4339ef7c4878c9b53b82ae4b0883e16544625016d6242"}, + {file = "grpcio-1.65.2-cp310-cp310-win32.whl", hash = "sha256:f931fe9b244dc23c7478c513c3ed94ded93da8bd1a95a4d97b21abdef644304a"}, + {file = "grpcio-1.65.2-cp310-cp310-win_amd64.whl", hash = "sha256:0c9c865d2fdf40e7e952038a0b5e0f32b01da84ecf04943b08e8917c8ccc9cf8"}, + {file = "grpcio-1.65.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:f4b7a7d68313e252e09550bd03d9d11e460dae681cf95588a131b6b3e07d1e30"}, + {file = "grpcio-1.65.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9ba9d4b3d4fc00b8083bb47a8c40a74ba3ea330713fdd59cf53c926c9a16b002"}, + {file = "grpcio-1.65.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b7bfcbee6b32f0e4786b7813692b3907c9e444f529126b8520cac9914479b98c"}, + {file = "grpcio-1.65.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aa50787bc8036bd5ea9b7ebbbd2c49c78122eb9ff98d3c217a7c146313c5030"}, + {file = "grpcio-1.65.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd7dc770926cc66050242eb6c63ca8ce12cd69010bf4ff7ea6e721d4f4b11e4d"}, + {file = "grpcio-1.65.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c45977fdc675a8961875adab7f04b785f65d3fd9c737cd60b5e3a9b1392ad444"}, + {file = "grpcio-1.65.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2a0cd7297abf0a02a9399edebe8c662058c7f0768bfbe859837707d389ad327f"}, + {file = "grpcio-1.65.2-cp311-cp311-win32.whl", hash = "sha256:60fe2f90875f2bef105158e370fbbefadd179f8cd689bc2cee6844aca4ccb7bb"}, + {file = "grpcio-1.65.2-cp311-cp311-win_amd64.whl", hash = "sha256:e0b2bf34340999c6d938107ec2cc9bce1ea59bf08e4694cfa47e782bdbd361f4"}, + {file = "grpcio-1.65.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:71fa3b7a6cef62a00014205d0e707610cfd50ae54f617d296017f10c6a9fad0d"}, + {file = "grpcio-1.65.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8856187a359a55babfa4d49ad96f2dd7edd8be3a36b813c7a9e41ef3d763400f"}, + {file = "grpcio-1.65.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:cb48342de1c3be59e6de79c6bbc01cf05562c571a3ed32f7c2e149e7934824cf"}, + {file = "grpcio-1.65.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b193e116e085ad4d7ef1518d79e9fedfa7688f4967f64a6246b5b196a26326a"}, + {file = "grpcio-1.65.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ce7f4c766fecc34455357b31b1e316506ea6ac48abbb9a650843d20337a2036"}, + {file = "grpcio-1.65.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:76125096d2a090d4acdce0f06f9511cebe1bcfbc0bd040e495563d7a8747dda1"}, + {file = "grpcio-1.65.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4fba3ae83ef5acd111c2dd92233ff167411db84e1ff17a00c34b5428355526c5"}, + {file = "grpcio-1.65.2-cp312-cp312-win32.whl", hash = "sha256:7fd639b0988ed5114d4b2a72ea453aafcb1439dd433c61834886b92afed9c6c1"}, + {file = "grpcio-1.65.2-cp312-cp312-win_amd64.whl", hash = "sha256:b6bba0f973ef6fe7434834f1b63d16bab4b50879d5bb0ca6eb0495c87d5cbc78"}, + {file = "grpcio-1.65.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:510bf7ec7f44e9420bb17970fb450522666d8b1c09cdf59b735de0c2dc806b79"}, + {file = "grpcio-1.65.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aacfd499d23130578184057008ea5329732a5ac59a4fcb73c0467d86723d23c8"}, + {file = "grpcio-1.65.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:67c5e5aa92b5832ae7a3399bce5b8562fb28686446732bfa17f97d5082e8501d"}, + {file = "grpcio-1.65.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7b752471e7ff1472ddbf3035a34fd8e24f2eac4fedbdab311e8f3e0dee889f7"}, + {file = "grpcio-1.65.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3101fa25b93f185e8cc698f8c2abee897891e6bae4f13472f66df21e8ae40d46"}, + {file = "grpcio-1.65.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:01600b1b02fdc9d648630d3de0a4cbf7ebe5f94b40ec1f65e3fd4b94a3b052cf"}, + {file = "grpcio-1.65.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8886d24345bf4b1693e9c09cf6a520f0baedd2af2a876f91bb508b24d0d46041"}, + {file = "grpcio-1.65.2-cp38-cp38-win32.whl", hash = "sha256:0b2ae6868864e4b06bff89cf91730a63141327158bf0677428ef315ea1dbdb0b"}, + {file = "grpcio-1.65.2-cp38-cp38-win_amd64.whl", hash = "sha256:c2900ad06fd8f5ad8832b1ee287caccb4a957e971b2b7983e0cd7a8e7c7098fb"}, + {file = "grpcio-1.65.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:06a7ea12a81e5e2fb17528556c7f828b90bd2aec3a645f5cd5f35f80aa59ac6a"}, + {file = "grpcio-1.65.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5edea0ea18e9fd5326d385a4c92a1fed605454e9a2c57ff131df0a08004b7e69"}, + {file = "grpcio-1.65.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d388f093010a014d3b3ddf8185ff45c5279fd825d0b20e21c8076515ae61db31"}, + {file = "grpcio-1.65.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5225b8ce980b598187f64436ed95ea149966d538253c28668347d331968e2386"}, + {file = "grpcio-1.65.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:892f03939df46d0bfcf89fe1dbcc8818f93ad6f3377587e8db6c2b1f598736c2"}, + {file = "grpcio-1.65.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:77fddf42bbca65ee4db679d0608e1ffa8b22b7f516c79665b7620be2f6357c85"}, + {file = "grpcio-1.65.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3a3139414399078560a84203f9fe3592483d902a2af84062c571be6191143a9f"}, + {file = "grpcio-1.65.2-cp39-cp39-win32.whl", hash = "sha256:8d6fd1206433428d0a4ba771eac70579b41a265fe835a4d8a5214c7235e69926"}, + {file = "grpcio-1.65.2-cp39-cp39-win_amd64.whl", hash = "sha256:478725160e2cfc1bfa5ab3e7bb7c896cc182c8f57255d780007cfd6fb46e97b5"}, + {file = "grpcio-1.65.2.tar.gz", hash = "sha256:e2c9bbb84d5517f2bccdb1836b8ee267a1757acb3cb3e575065c103220b577ac"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.65.1)"] +protobuf = ["grpcio-tools (>=1.65.2)"] [[package]] name = "http-client" -version = "2.1.15" +version = "2.1.16" description = "Balancing http client around aiohttp" optional = false python-versions = "~=3.9" @@ -576,8 +576,8 @@ yarl = "1.9.2" [package.source] type = "git" url = "https://github.com/hhru/balancing-http-client.git" -reference = "2.1.15" -resolved_reference = "4cb3b320e7b4fd2c5e4fc05ed42acd981c89ae25" +reference = "2.1.16" +resolved_reference = "5377c8e1efd9ca5f8752bd8fb02131859b940142" [[package]] name = "idna" @@ -2000,4 +2000,4 @@ testing = ["aioresponses", "tornado-httpclient-mock"] [metadata] lock-version = "2.0" python-versions = "~=3.9" -content-hash = "1c5b2fc2edeea79c99a2ca3f1788ad557b6ec1ce882f141c1d14cad786104fe0" +content-hash = "270cf80fa3b6574eac3cea2b10131878be7934d43d8f5a95e42a047de03c0758" diff --git a/pyproject.toml b/pyproject.toml index e4cce0f85..d2c89e17b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ lxml = '4.9.2' pydantic = '^2.3.0' tornado = '6.3.3' orjson = '*' -http-client = {git = 'https://github.com/hhru/balancing-http-client.git', tag = '2.1.15'} +http-client = {git = 'https://github.com/hhru/balancing-http-client.git', tag = '2.1.16'} python-consul2-hh = {git = 'https://github.com/hhru/python-consul2', tag = 'v0.2.10'} opentelemetry-sdk = '1.25.0' opentelemetry-api = '1.25.0' diff --git a/tests/projects/re_app/__init__.py b/tests/projects/re_app/__init__.py index 4486aa9bc..0872438a3 100644 --- a/tests/projects/re_app/__init__.py +++ b/tests/projects/re_app/__init__.py @@ -1,15 +1,11 @@ import jinja2 from frontik.app import FrontikApplication -from frontik.handler import get_default_headers from frontik.options import options from frontik.util import get_abs_path class TestApplication(FrontikApplication): - async def application_404_handler(self, _request): - return 404, get_default_headers(), b'404' - def get_jinja_environment(self): env = jinja2.Environment( loader=jinja2.FileSystemLoader(get_abs_path(self.app_root, options.jinja_template_root)), diff --git a/tests/projects/re_app/pages/handler_404.py b/tests/projects/re_app/pages/handler_404.py index 276f5bafb..0873a987b 100644 --- a/tests/projects/re_app/pages/handler_404.py +++ b/tests/projects/re_app/pages/handler_404.py @@ -1,7 +1,8 @@ from frontik.handler import PageHandler, get_current_handler -from frontik.routing import regex_router +from frontik.routing import not_found_router, regex_router +@not_found_router.get('__not_found', cls=PageHandler) @regex_router.get('/id/(?P[^/]+)/(?P[^/]+)', cls=PageHandler) async def get_page(handler=get_current_handler()): handler.text = '404' diff --git a/tests/test_fail_fast.py b/tests/test_fail_fast.py index c543dbf9f..41e3aff37 100644 --- a/tests/test_fail_fast.py +++ b/tests/test_fail_fast.py @@ -20,7 +20,7 @@ def test_fail_fast(self): def test_fail_fast_unknown_method(self): response = frontik_test_app.get_page('fail_fast?fail_fast=true', method=requests.head) - assert response.status_code == 405 + assert response.status_code == 401 def test_fail_fast_without_done(self): response = frontik_test_app.get_page('fail_fast/fail_fast_without_done')