1

Been scrolling though every question on StackOverflow and Youtube to find an answer. I'm deploying both WSGI and ASGI/websockets via Django channels on Heroku. It works locally, but it's been giving me trouble during production.

Setup

Here's my procfile:

web: daphne API.asgi:application --port $PORT --bind 0.0.0.0 -v2
worker: python manage.py runworker background -v2

Settings.py:

# Redis Setup
ASGI_APPLICATION = 'API.routing.application'
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [(HEROKU_URL, 6379)],
        },
    },
}

asgi.py:

import os
import django
from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'API.settings')
django.setup()
application = get_asgi_application()

And finally, routing.py:

application = ProtocolTypeRouter({
    'websocket':AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter(
                [
                    url(r"^timer/$", TestConsumer.as_asgi()),
                ]
            )
        )
    )
    ,'channel':ChannelNameRouter({
        'background':BackgroundConsumer.as_asgi()
    })
}) 

Problem/logs

Heroku logs when connecting to the websocket:

2021-07-02T19:42:42.209398+00:00 app[web.1]: IP - - [02/Jul/2021:19:42:42] "GET /chat_switch/1/" 200 10480
2021-07-02T19:43:02.708851+00:00 app[web.1]: IP - - [02/Jul/2021:19:43:02] "WSCONNECTING /timer/" - -
2021-07-02T19:43:02.709658+00:00 app[web.1]: 2021-07-02 19:43:02,709 DEBUG    Upgraded connection [IP, 32183] to WebSocket
2021-07-02T19:43:03.058159+00:00 app[web.1]: 2021-07-02 19:43:03,057 ERROR    Exception inside application: Django can only handle ASGI/HTTP connections, not websocket.
2021-07-02T19:43:03.058167+00:00 app[web.1]: Traceback (most recent call last):
2021-07-02T19:43:03.058168+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.8/site-packages/django/core/handlers/asgi.py", line 143, in __call__
2021-07-02T19:43:03.058168+00:00 app[web.1]:     raise ValueError(
2021-07-02T19:43:03.058169+00:00 app[web.1]: ValueError: Django can only handle ASGI/HTTP connections, not websocket.
2021-07-02T19:43:03.059813+00:00 app[web.1]: 2021-07-02 19:43:03,059 INFO     failing WebSocket opening handshake ('Internal server error')
2021-07-02T19:43:03.060494+00:00 app[web.1]: 2021-07-02 19:43:03,060 WARNING  dropping connection to peer tcp4:IP:32183 with abort=False: Internal server error
2021-07-02T19:43:03.064484+00:00 app[web.1]: 2021-07-02 19:43:03,063 DEBUG    WebSocket closed for [IP, 32183]

Other than that, the regular API functions work as intended.

What I've tried

If anyone knows what I can do from here, I'd appreciate it.

1 Answer 1

2

It doesn't look like application in routing.py is doing anything. I see it specified in ASGI_APPLICATION but, I believe, that's used by the development server (manage.py runserver). In your daphne command, you're serving API.asgi:application which only defines the django ASGI app - the "http" connection handler.

What you need is to replace get_asgi_application() with a ProtocolTypeRouter which defines how each protocol is served - "http" goes to the django ASGI application and "websocket" goes to your consumer routes.

Take a look at the Deploying section in Channels documentation. Your asgi.py could look something like this (I didn't include your consumer imports):

import os

from django.core.asgi import get_asgi_application
from django.urls import path

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'API.settings')

django_asgi_app = get_asgi_application()

from channels.auth import AuthMiddlewareStack
from channels.security.websocket import AllowedHostsOriginValidator
from channels.routing import ProtocolTypeRouter, URLRouter, ChannelNameRouter

application = ProtocolTypeRouter({
    'http': django_asgi_app,
    'websocket': AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter(
                [
                    path('timer/', TestConsumer.as_asgi()),
                ]
            )
        )
    ),
    'channel': ChannelNameRouter({
        'background': BackgroundConsumer.as_asgi()
    })
})
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.