diff --git a/channels/routing.py b/channels/routing.py index 07b5bab7..d09d5ffe 100644 --- a/channels/routing.py +++ b/channels/routing.py @@ -107,6 +107,15 @@ async def __call__(self, scope, receive, send): path = scope.get("path_remaining", scope.get("path", None)) if path is None: raise ValueError("No 'path' key in connection scope, cannot route URLs") + + if "path_remaining" not in scope: + # We are the outermost URLRouter, so handle root_path if present. + root_path = scope.get("root_path", "") + if root_path and not path.startswith(root_path): + # If root_path is present, path must start with it. + raise ValueError("No route found for path %r." % path) + path = path[len(root_path) :] + # Remove leading / to match Django's handling path = path.lstrip("/") # Run through the routes we have until one matches diff --git a/tests/test_routing.py b/tests/test_routing.py index b41f652a..99c76790 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -107,6 +107,23 @@ async def test_url_router(): "args": tuple(), "kwargs": {"default": 42}, } + # Valid root_path in scope + assert ( + await router( + {"type": "http", "path": "/root/", "root_path": "/root"}, None, None + ) + == 1 + ) + assert ( + await router( + {"type": "http", "path": "/root/foo/", "root_path": "/root"}, None, None + ) + == 2 + ) + + # Unmatched root_path in scope + with pytest.raises(ValueError): + await router({"type": "http", "path": "/", "root_path": "/root"}, None, None) # Invalid matches with pytest.raises(ValueError): @@ -261,6 +278,29 @@ async def test_path_remaining(): == 2 ) + assert ( + await outermost_router( + {"type": "http", "path": "/root/prefix/stuff/", "root_path": "/root"}, + None, + None, + ) + == 2 + ) + + with pytest.raises(ValueError): + await outermost_router( + {"type": "http", "path": "/root/root/prefix/stuff/", "root_path": "/root"}, + None, + None, + ) + + with pytest.raises(ValueError): + await outermost_router( + {"type": "http", "path": "/root/prefix/root/stuff/", "root_path": "/root"}, + None, + None, + ) + def test_invalid_routes(): """