Handling of `scope["root_path"]` and `scope["path"]` differs from ASGI spec
Checklist
- [x] The bug is reproducible against the latest release and/or
master. - [x] There are no similar issues or pull requests to fix it yet.
Describe the bug
Accoring to https://github.com/django/asgiref/issues/229#issuecomment-765583185, scope["path"] includes the scope["root_path"].
So if root_path is /foo and request for /foo/bar has come, the upstream application servers (such as uvicorn) are passing scope["path"] == "/foo/bar" and scope["root_path"] == "/foo", which is correct behavior.
But starlette does not conform this. It assumes scope["path"] is a remainder of stripping scope["root_path"] from original request path. In this case, scope["path"] == "/bar" and scope["root_path"] == "/foo". This ASGI incompatible assumption can be seen here or here and many other places.
The following "To reproduce" section shows just one aspect of this wrong assumption.
To reproduce
- Save
main.pyas follows.
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route, Mount
class Bar:
async def __call__(self, scope, receive, send):
await PlainTextResponse(f"bar {scope['root_path']=} {scope['path']=}")(scope, receive, send)
class FooBar:
async def __call__(self, scope, receive, send):
await PlainTextResponse(f"foobar {scope['root_path']=} {scope['path']=}")(scope, receive, send)
routes =[
Route('/bar', endpoint=Bar()),
Mount('/foo', routes=[
Route('/bar', endpoint=FooBar())
])
]
app = Starlette(routes=routes)
- Run
uvicorn main:app --port 8000 --root-path /foo - Access by
curl http://localhost:8000/foo/bar
Expected behavior
Receives
bar scope['root_path']='/foo' scope['path']='/foo/bar'
Actual behavior
Receives
foobar scope['root_path']='/foo/foo' scope['path']='/bar'
Some points here are,
-
scope['path']does not includescope['root_path'], which is ASGI incompatible. -
FooBarASGI handler is called. It does not takescope['root_path']into account when routing.
Environment
- OS: Linux
- Python version: 3.8.2
- Starlette version: 0.17.0
Additional context
This may be a root cause of this issue in fastapi
Hello @Kludex :wave: I would be interested to help tackling this issue; if you need a hand, let me know :)
Take in consideration that I'll need a lot of references to follow on this, and to understand the implications for our users.
But yeah, help is appreciated. 🙏