Skip to content

Websocket returning 500 instead of 4XX when unable to resolve the path #2147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
sprezz-arthur opened this issue Apr 5, 2025 · 3 comments

Comments

@sprezz-arthur
Copy link

sprezz-arthur commented Apr 5, 2025

What is the expected behavior when attempting to access a path not available in websocket_urlpatterns?
Currently my application will cause a status code 500 and I would hope to have a 404 (or 403) instead.

HOW TO REPRODUCE:

Now connecting to a room using int will behave normally:

  • Create a room named 42: WebSocket CONNECT /ws/chat/42/ [127.0.0.1:57662]

But when attempting to create a room with a non-int name, the behavior seems strange.

EXPECTED BEHAVIOR

  • Create a room named "banana": WebSocket REJECT /ws/chat/banana/. (status 403)

CURRENT BEHAVIOR

  • Create a room named "banana": ValueError: No route found for path 'ws/chat/banana/'. (status 500)
@sprezz-arthur
Copy link
Author

Currently I'm getting away with this RouteValidator class.

But it seems rather brittle and I suppose there's a reason this is not the default behavior.

# mysite/asgi.py
import os

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator, WebsocketDenier
from channels.middleware import BaseMiddleware
from django.core.asgi import get_asgi_application
from django.urls import Resolver404

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
# Initialize Django ASGI application early to ensure the AppRegistry
# is populated before importing code that may import ORM models.
django_asgi_app = get_asgi_application()

from chat.routing import websocket_urlpatterns


class RouteValidator(BaseMiddleware):
    async def __call__(self, *args, **kwargs):
        try:
            await self.inner(*args, **kwargs)
        except (Resolver404, ValueError) as e:
            if "No route found for path" in str(e):
                return await WebsocketDenier()(*args, **kwargs)
            raise

application = ProtocolTypeRouter(
    {
        "http": django_asgi_app,
        "websocket": AllowedHostsOriginValidator(
            AuthMiddlewareStack(RouteValidator(URLRouter(websocket_urlpatterns)))
        ),
    }
)

@sprezz-arthur sprezz-arthur changed the title Is it possible to have a websocket return 404 Not Found? Websocket returning 500 instead of 4XX when unable to resolve the path Apr 5, 2025
@carltongibson
Copy link
Member

The behaviour here has been the same since URLRouter was first added: a1044b4

I'm not immediately sure about a change here. We'd have to think that through.

(Why 403 as expected, not 404?)

@sprezz-arthur
Copy link
Author

sprezz-arthur commented Apr 5, 2025

Thanks for the quick feedback.
I'm concerned whether I'm missing out on some of the reasoning, as it seems very deliberate choice to raise ValueError and cause 500 status code, instead of rejecting the handshake and causing some 4XX status code.

As for the 404 vs 403, at first I attempted to get a 404 out of it, but I couldn't manage to do so.
On the other hand, in order to get a 403, all I had to do is close the websocket in a try-except wrapper (the condition seems very brittle as I said though), similarly to how it's done on the middleware AllowedHostsOriginValidator in a rather straightforward approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants