From ec8e3a2ba759ed36bd267502572eb6dfb640d524 Mon Sep 17 00:00:00 2001 From: Alex Goncharov Date: Sat, 18 May 2024 11:24:57 +0300 Subject: [PATCH] fix: use re.match instead of re.search for mounted app path (#3501) --- litestar/_asgi/routing_trie/traversal.py | 4 +- .../test_routing_trie/test_traversal.py | 93 +++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 tests/unit/test_asgi/test_routing_trie/test_traversal.py diff --git a/litestar/_asgi/routing_trie/traversal.py b/litestar/_asgi/routing_trie/traversal.py index a99c75d73e..ad249fe202 100644 --- a/litestar/_asgi/routing_trie/traversal.py +++ b/litestar/_asgi/routing_trie/traversal.py @@ -136,8 +136,8 @@ def parse_path_to_route( asgi_app, handler = parse_node_handlers(node=root_node.children[path], method=method) return asgi_app, handler, path, {} - if mount_paths_regex and (match := mount_paths_regex.search(path)): - mount_path = path[match.start() : match.end()] + if mount_paths_regex and (match := mount_paths_regex.match(path)): + mount_path = path[: match.end()] mount_node = mount_routes[mount_path] remaining_path = path[match.end() :] # since we allow regular handlers under static paths, we must validate that the request does not match diff --git a/tests/unit/test_asgi/test_routing_trie/test_traversal.py b/tests/unit/test_asgi/test_routing_trie/test_traversal.py new file mode 100644 index 0000000000..a92995750d --- /dev/null +++ b/tests/unit/test_asgi/test_routing_trie/test_traversal.py @@ -0,0 +1,93 @@ +from typing import Any + +from litestar import Router, asgi, get +from litestar.response.base import ASGIResponse +from litestar.status_codes import HTTP_404_NOT_FOUND +from litestar.testing import create_test_client + + +def test_parse_path_to_route_mounted_app_path_root() -> None: + # test that paths are correctly dispatched to handlers when mounting an app + # and other handlers to root path / + + @asgi("/foobar", is_mount=True) + async def mounted_handler(scope: Any, receive: Any, send: Any) -> None: + response = ASGIResponse(body="mounted") + await response(scope, receive, send) + + @get("/{number:int}/foobar/") + async def parametrized_handler() -> str: + return "parametrized" + + @get("/static/foobar/") + async def static_handler() -> str: + return "static" + + with create_test_client( + [ + mounted_handler, + parametrized_handler, + static_handler, + ] + ) as client: + response = client.get("/foobar") + assert response.text == "mounted" + + response = client.get("/foobar/123/") + assert response.text == "mounted" + + response = client.get("/123/foobar/") + assert response.text == "parametrized" + + response = client.get("/static/foobar/") + assert response.text == "static" + + response = client.get("/unknown/foobar/") + assert response.status_code == HTTP_404_NOT_FOUND + + +def test_parse_path_to_route_mounted_app_path_router() -> None: + # test that paths are correctly dispatched to handlers when mounting an app + # and other handlers inside subrouter + + @asgi("/foobar", is_mount=True) + async def mounted_handler(scope: Any, receive: Any, send: Any) -> None: + response = ASGIResponse(body="mounted") + await response(scope, receive, send) + + @get("/{number:int}/foobar/") + async def parametrized_handler() -> str: + return "parametrized" + + @get("/static/foobar/") + async def static_handler() -> str: + return "static" + + sub_router = Router( + path="/sub", + route_handlers=[ + mounted_handler, + parametrized_handler, + static_handler, + ], + ) + base_router = Router(path="/base", route_handlers=[sub_router]) + + with create_test_client([base_router]) as client: + response = client.get("/foobar") + assert response.status_code == HTTP_404_NOT_FOUND + + response = client.get("/base/sub/foobar") + assert response.text == "mounted" + + response = client.get("/base/sub/foobar/123/") + assert response.text == "mounted" + + response = client.get("/base/sub/123/foobar/") + assert response.text == "parametrized" + + response = client.get("/base/sub/static/foobar/") + assert response.text == "static" + + response = client.get("/base/sub/unknown/foobar/") + assert response.status_code == HTTP_404_NOT_FOUND