diff --git a/CHANGES/3701.bugfix b/CHANGES/3701.bugfix new file mode 100644 index 00000000000..d6734bd70eb --- /dev/null +++ b/CHANGES/3701.bugfix @@ -0,0 +1 @@ +Fix overshadowing of overlapped subbaps prefixes. \ No newline at end of file diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 07a9b81347f..36323a59a3e 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -84,6 +84,7 @@ Eric Sheng Erich Healy Eugene Chernyshov Eugene Naydenov +Eugene Nikolaiev Eugene Tolmachev Evert Lammerts FichteFoll diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 1bd4fd8dc8c..7cdadbeb0e4 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -698,7 +698,8 @@ def get_info(self) -> Dict[str, Any]: 'prefix': self._prefix} async def resolve(self, request: Request) -> _Resolve: - if not request.url.raw_path.startswith(self._prefix): + if not request.url.raw_path.startswith(self._prefix + '/') and \ + request.url.raw_path != self._prefix: return None, set() match_info = await self._app.router.resolve(request) match_info.add_app(self._app) diff --git a/tests/test_urldispatch.py b/tests/test_urldispatch.py index 632fd275d3c..66e6caf831c 100644 --- a/tests/test_urldispatch.py +++ b/tests/test_urldispatch.py @@ -1246,3 +1246,47 @@ def test_prefixed_subapp_resource_canonical(app) -> None: subapp = web.Application() res = subapp.add_subapp(canonical, subapp) assert res.canonical == canonical + + +async def test_prefixed_subapp_overlap(app) -> None: + """ + Subapp should not overshadow other subapps with overlapping prefixes + """ + subapp1 = web.Application() + handler1 = make_handler() + subapp1.router.add_get('/a', handler1) + app.add_subapp('/s', subapp1) + + subapp2 = web.Application() + handler2 = make_handler() + subapp2.router.add_get('/b', handler2) + app.add_subapp('/ss', subapp2) + + match_info = await app.router.resolve(make_mocked_request('GET', '/s/a')) + assert match_info.route.handler is handler1 + match_info = await app.router.resolve(make_mocked_request('GET', '/ss/b')) + assert match_info.route.handler is handler2 + + +async def test_prefixed_subapp_empty_route(app) -> None: + subapp = web.Application() + handler = make_handler() + subapp.router.add_get('', handler) + app.add_subapp('/s', subapp) + + match_info = await app.router.resolve(make_mocked_request('GET', '/s')) + assert match_info.route.handler is handler + match_info = await app.router.resolve(make_mocked_request('GET', '/s/')) + assert "" == repr(match_info) + + +async def test_prefixed_subapp_root_route(app) -> None: + subapp = web.Application() + handler = make_handler() + subapp.router.add_get('/', handler) + app.add_subapp('/s', subapp) + + match_info = await app.router.resolve(make_mocked_request('GET', '/s/')) + assert match_info.route.handler is handler + match_info = await app.router.resolve(make_mocked_request('GET', '/s')) + assert "" == repr(match_info)