Skip to content

StaticFiles causes Internal Server Error when user accesses existing files as directory #1123

@xKerman

Description

@xKerman

Checklist

  • The bug is reproducible against the latest release and/or master.
  • There are no similar issues or pull requests to fix it yet.

Describe the bug

StaticFiles causes Internal Server Error when user accesses existing files as directory (e.g. /static/somefile.txt/foobar)

To reproduce

  1. create virtual env and activate it with python -m venv venv && source venv/bin/activate
  2. install dependencies with pip install starlette uvicorn aiofiles
  3. setup application as follows:

directory structure:

.
├── poc.py
├── static
│   └── sample.txt
└── venv

poc.py

# code from https://www.starlette.io/staticfiles/
from starlette.applications import Starlette
from starlette.routing import Mount
from starlette.staticfiles import StaticFiles


routes = [
    Mount("/static", app=StaticFiles(directory="static"), name="static"),
]

app = Starlette(routes=routes)
  1. run application with uvicorn poc:app
  2. access http://127.0.0.1:8000/static/sample.txt/foo

Expected behavior

StaticFiles returns "404 Not Found" HTTP response (as Apache HTTP Server and Nginx does).

Actual behavior

StaticFiles returns "500 Internal Server Error" HTTP response.

Debugging material

console log with tracebacks:

INFO:     Started server process [13052]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     127.0.0.1:64288 - "GET /static/sample.txt/foo HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/Users/xkhorasan/programs/test_starlette/venv/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py", line 394, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/Users/xkhorasan/programs/test_starlette/venv/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/Users/xkhorasan/programs/test_starlette/venv/lib/python3.9/site-packages/starlette/applications.py", line 112, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/xkhorasan/programs/test_starlette/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/Users/xkhorasan/programs/test_starlette/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/Users/xkhorasan/programs/test_starlette/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
    raise exc from None
  File "/Users/xkhorasan/programs/test_starlette/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/Users/xkhorasan/programs/test_starlette/venv/lib/python3.9/site-packages/starlette/routing.py", line 582, in __call__
    await route.handle(scope, receive, send)
  File "/Users/xkhorasan/programs/test_starlette/venv/lib/python3.9/site-packages/starlette/routing.py", line 392, in handle
    await self.app(scope, receive, send)
  File "/Users/xkhorasan/programs/test_starlette/venv/lib/python3.9/site-packages/starlette/staticfiles.py", line 97, in __call__
    response = await self.get_response(path, scope)
  File "/Users/xkhorasan/programs/test_starlette/venv/lib/python3.9/site-packages/starlette/staticfiles.py", line 114, in get_response
    full_path, stat_result = await self.lookup_path(path)
  File "/Users/xkhorasan/programs/test_starlette/venv/lib/python3.9/site-packages/starlette/staticfiles.py", line 154, in lookup_path
    stat_result = await aio_stat(full_path)
  File "/Users/xkhorasan/programs/test_starlette/venv/lib/python3.9/site-packages/aiofiles/os.py", line 13, in run
    return await loop.run_in_executor(executor, pfunc)
  File "/usr/local/Cellar/[email protected]/3.9.1_2/Frameworks/Python.framework/Versions/3.9/lib/python3.9/concurrent/futures/thread.py", line 52, in run
    result = self.fn(*self.args, **self.kwargs)
NotADirectoryError: [Errno 20] Not a directory: '/Users/xkhorasan/programs/test_starlette/static/sample.txt/foo'

Environment

  • OS: macOS
  • Python version: 3.9.1
  • Starlette version: 0.14.1

Additional context

Apache HTTP Server and Nginx treat this case (access existing file as directory) as "404 Not Found".
Samples:

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions