Websockets with Django Channels on Heroku

2024/10/11 0:28:52

I am trying to deploy my app to heroku. The app has a simple chatting system that uses Websockets and django channels.

When I test my app using python manage.py runserver the app behaves just as intended.

I tried deploying the app and all features work except for the chatting system.

Here is the error message I am getting in the Chrome Console:

layout.js:108 Mixed Content: The page at 'https://desolate-lowlands-74512.herokuapp.com/index' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://desolate-lowlands-74512.herokuapp.com/ws/chat/19/'. This request has been blocked; this endpoint must be available over WSS.

This is what I tried to fix it: I went from ws to wss I changed this:

 const chatSocket = new WebSocket('ws://'+ window.location.host+ '/ws/chat/'+ friendship_id+ '/');console.log(chatSocket)

to this:

 const chatSocket = new WebSocket('wss://'+ window.location.host+ '/ws/chat/'+ friendship_id+ '/');console.log(chatSocket)

with this change the websocket loads but the chatting system still doesn't work. The messages still don't get sent or received

This is the error message I get when opening the Chatbox:

layout.js:108 WebSocket connection to 'wss://desolate-lowlands-74512.herokuapp.com/ws/chat/19/' failed: Error during WebSocket handshake: Unexpected response code: 404

and this is the error message I get when I try to send the message:

WebSocket is already in CLOSING or CLOSED state.

Here is the code: This is my asgi.py file:

"""
ASGI config for DBSF project.It exposes the ASGI callable as a module-level variable named ``application``.For more information on this file, see
https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
"""import osfrom django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import social.routingos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DBSF.settings')application = ProtocolTypeRouter({"http": get_asgi_application(),"websocket": AuthMiddlewareStack(URLRouter(social.routing.websocket_urlpatterns)),
})

here is routing.py

from django.urls import re_path
from . import consumerswebsocket_urlpatterns = [re_path(r'ws/chat/(?P<friendship_id>\w+)/$', consumers.ChatConsumer.as_asgi()),
]

Here's consumers.py

import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
from .models import Message, Friendship, User
import datetimeclass ChatConsumer(WebsocketConsumer):def connect(self):self.room_name = self.scope['url_route']['kwargs']['friendship_id']self.room_group_name = 'chat_%s' % self.room_name# Join room groupasync_to_sync(self.channel_layer.group_add)(self.room_group_name,self.channel_name)self.accept()def disconnect(self, close_code):# Leave room groupasync_to_sync(self.channel_layer.group_discard)(self.room_group_name,self.channel_name)# Receive message from WebSocketdef receive(self, text_data):text_data_json = json.loads(text_data)message = text_data_json['message']sender = text_data_json['sender']receiver = text_data_json['receiver']friendship_id = self.scope['url_route']['kwargs']['friendship_id']message_to_save = Message(conversation=Friendship.objects.get(id=friendship_id), sender=User.objects.get(username=sender), receiver=User.objects.get(username=receiver), text=message, date_sent=datetime.datetime.now())message_to_save.save()# Send message to room groupasync_to_sync(self.channel_layer.group_send)(self.room_group_name,{'type': 'chat_message','message': message,'sender': sender,'receiver': receiver,'id': message_to_save.id})# Receive message from room groupdef chat_message(self, event):message = event['message']sender = event['sender']receiver = event['receiver']id = event['id']# Send message to WebSocketself.send(text_data=json.dumps({'message': message,'sender': sender,'receiver': receiver,'id': id,}))

Here is settings.py

"""
Django settings for DBSF project.Generated by 'django-admin startproject' using Django 3.1.2.For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""from pathlib import Path
import django_heroku
import os
from dotenv import load_dotenv
load_dotenv()
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ['SECRET_KEY']
AUTH_USER_MODEL = 'social.User'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['desolate-lowlands-74512.herokuapp.com', 'localhost', '127.0.0.1']# Application definitionINSTALLED_APPS = ['channels','social','django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles',
]MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','whitenoise.middleware.WhiteNoiseMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',
]ROOT_URLCONF = 'DBSF.urls'TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [],'APP_DIRS': True,'OPTIONS': {'context_processors': ['django.template.context_processors.debug','django.template.context_processors.request','django.contrib.auth.context_processors.auth','django.contrib.messages.context_processors.messages',],},},
]REST_FRAMEWORK = {# Use Django's standard `django.contrib.auth` permissions,# or allow read-only access for unauthenticated users.'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly']
}WSGI_APPLICATION = 'DBSF.wsgi.application'
ASGI_APPLICATION = 'DBSF.asgi.application'
CHANNEL_LAYERS = {'default': {'BACKEND': 'channels_redis.core.RedisChannelLayer','CONFIG': {"hosts": [('127.0.0.1', 6379)],},},
}# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databasesDATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3','NAME': BASE_DIR / 'db.sqlite3',}
}# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validatorsAUTH_PASSWORD_VALIDATORS = [{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',},{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',},{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',},{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',},
]# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/LANGUAGE_CODE = 'en-us'TIME_ZONE = 'EST'USE_I18N = TrueUSE_L10N = TrueUSE_TZ = True# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'
MEDIA_ROOT= os.path.join(BASE_DIR, 'media/')
MEDIA_URL= "/media/"

here's wsgi.py

"""
WSGI config for DBSF project.It exposes the WSGI callable as a module-level variable named ``application``.For more information on this file, see
https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
"""import osfrom django.core.wsgi import get_wsgi_applicationos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DBSF.settings')application = get_wsgi_application()

I assume the once I change from ws to wss the consumer can't connect. I think this will be an easy fix but I can't figure out what code i need to change. I suspect that it is something in asgy.py or routing.py

Please let me know if it is unclear what I am asking or if I need to show any other files

Answer

Question details

Hello, here is some steps that I find to myself to deploy similar mysite app for heroku server. I used this libraries:

django==3.2.6
channels==3.0.4
python==3.9.6

Channels library

Quick start

You can read how to work with it at Tutorial. First of all, follow the installation and tutorial.

In your Procfile you need to use daphne which is installed with channels library by default:

web: daphne -b 0.0.0.0 -p $PORT mysite.asgi:application

You don't need any workers if you use latest version of channels.

At heroku environment $PORT replace with some port that it provide. You can provide it locally by using .env file.

The last thing you need to replace ws:// with wss:// (see Errors and solutions) at room.html.

Errors and solutions

Unexpected response code: 200 (or other code XXX) (solved):

  • Be sure you include your application and channels via settings (mysite.settings) and use asgi application:
INSTALLED_APPS = ['channels','chat',...
]
...
ASGI_APPLICATION = "mysite.asgi.application"
  • Be sure you use channel layers (mysite.settings).
CHANNEL_LAYERS = {'default': {'BACKEND': 'channels.layers.InMemoryChannelLayer',},
}

According Documentation you should use database for production, but for local environment you may use channels.layers.InMemoryChannelLayer.

  • Be sure you run asgi server (not wsgi) because you need asynchronous behaviour. Also, for deployment you should use daphne instead of gunicorn. daphne is included in channels library by default, so you don't need to install it manually. Basic run server will look like (Terminal):
daphne -b 0.0.0.0 -p $PORT mysite.asgi:application

where $PORT is specific port (for UNIX system is 5000). (That format used for heroku application, you can change it manually).

Error in connection establishment: net::ERR_SSL_PROTOCOL_ERROR and same errors with using https connection (solved):

Difference between ws and wss?

You may think about to use your server via wss protocol: replace ws://... with wss://... or use following template in your html (chat/templates/chat/room.html):

(window.location.protocol === 'https:' ? 'wss' : 'ws') + '://'

Hope this answer is useful for channels with Django.

https://en.xdnf.cn/q/69835.html

Related Q&A

How can I get the name/file of the script from sitecustomize.py?

When I run any Python script, I would like to see the scripts filename appear in the Windows command line windows titlebar. For example, if I run a script called "mytest.py", I want to see &q…

Sending Godaddy email via Django using python

I have these settings EMAIL_HOST = smtpout.secureserver.net EMAIL_HOST_USER = [email protected] EMAIL_HOST_PASSWORD = password DEFAULT_FROM_EMAIL = [email protected] SERVER_EMAIL = [email protected] EM…

python math, numpy modules different results?

I get slightly different results calculating the cosine of a value. How can I check that this difference is within machine precision?import math math.cos(60.0/180.0*math.pi) -> 0.5000000000000001im…

How to extract equation from a polynomial fit?

My goal is to fit some data to a polynomial function and obtain the actual equation including the fitted parameter values. I adapted this example to my data and the outcome is as expected. Here is my c…

python dictionary conundrum

On the console I typed in>>> class S(str): pass ... >>> a = hello >>> b = S(hello) >>> d = {a:a, b:b} >>> d {hello: hello} >>> type(d[a]) <class…

Celery 4 not auto-discovering tasks

I have a Django 1.11 and Celery 4.1 project, and Ive configured it according to the setup docs. My celery_init.py looks likefrom __future__ import absolute_importimport osfrom celery import Celery# set…

Is it possible to use a custom filter function in pandas?

Can I use my helper function to determine if a shot was a three pointer as a filter function in Pandas? My actual function is much more complex, but i simplified it for this question.def isThree(x, y…

what is request in Django view

In the Django tutorial for the first app in Django we havefrom django.http import HttpResponsedef index(request):return HttpResponse("Hello, world. Youre at the polls index.")And then the url…

Cannot update python package on anaconda to latest version

Some of my python packages on anaconda cannot be updated to the latest version.For instance, beautifulsoup4 latest version on anaconda is v4.71 as seen in the release notes. https://docs.anaconda.com/a…

How does tensorflow.pad work?

There is the example of tensorflow.pad():# t = is [[1, 2, 3], [4, 5, 6]]. # paddings is [[1, 1,], [2, 2]]. # rank of t is 2.tf.pad(t, paddings, "CONSTANT") ==> [[0, 0, 0, 0, 0, 0, 0],[…