I've checked all the related questions none of them helped me. I'm making a simple application where I will get JSON responses on the websocketking.com site. No matter how many times I make changes none of the tries are working.
here every detail
asgi.py file
import os
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from django.urls import path
from home.consumers import *
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
ws_patterns = [
path('ws/test/', TestConsumer),
]
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(URLRouter(ws_patterns)),
})
consumers.py file
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
import json
class TestConsumer(WebsocketConsumer):
def connect(self):
self.room_name = "test_consumer"
self.room_group_name = "test_consumer_group"
async_to_sync(self.channel_layer.group_add)(self.room_name, self.room_group_name)
self.accept()
self.send(text_data=json.dumps({'status' : 'connected'}))
def receive(self):
pass
def disconnect(self):
pass
These are the things I added in my settings.py file
ASGI_APPLICATION = 'core.asgi.application'
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("localhost", 6379)],
},
},
}
And here is the error I got in console
WebSocket HANDSHAKING /ws/test/ [127.0.0.1:2795]
Exception inside application: object.__init__() takes exactly one argument (the instance to initialize)
Traceback (most recent call last):
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\staticfiles.py", line 44, in __call__
return await self.application(scope, receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\routing.py", line 71, in __call__
return await application(scope, receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\sessions.py", line 47, in __call__
return await self.inner(dict(scope, cookies=cookies), receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\sessions.py", line 254, in __call__
return await self.inner(wrapper.scope, receive, wrapper.send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\auth.py", line 181, in __call__
return await super().__call__(scope, receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\middleware.py", line 26, in __call__
return await self.inner(scope, receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\routing.py", line 150, in __call__
return await application(
File "D:\PythonProjects\channels_project\env\lib\site-packages\asgiref\compatibility.py", line 33, in new_application
instance = application(scope)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\generic\websocket.py", line 23, in __init__
super().__init__(*args, **kwargs)
TypeError: object.__init__() takes exactly one argument (the instance to initialize)
WebSocket DISCONNECT /ws/test/ [127.0.0.1:2795]
Maybe you've already solved this issue but here's some things I've noticed with your code which might help you:
Change WebsocketConsumer to AsyncWebsocketConsumer- Then change your consumer methods to async, e.g.
async def connect(self):
...
Change
async_to_sync(self.channel_layer.group_add)(self.room_name,
self.room_group_name)
to
await self.channel_layer.group_add(
self.room_name,
self.room_group_name)
As a side note:
You don't need to do self.send(msg) to check if you are connected. This is handled in self.accept() and will return to
client.onopen = () => {
console.log("connected");
}
If none of these sort your problem it may be an issue with your routing and/or the middleware stack. I have had issues with authentication recently. Try using a different browser if you're using Chrome and see if you can tag onto the onerror event
Related
I am coding telegram-bot, using Flask and asyncio. And I have a function for receiving and processing requests - get_messages(). And test function - sleep_func(), which will sleep for a while(20 seconds), but get_messages() will be working at this moment, when sleep_func() is sleeping. After 20 seconds sleep_func() must wake up, print some text, but doesn't finish the get_messages(). So, I want to implement asynchrony. I tried to do this, using asyncio.create_task, that worked, but only for one request(see at the comments in get_messages()). Now I tried another way, but it gives me this error:
ERROR:asyncio:Task exception was never retrieved
future: <Task finished name='Task-7' coro=<get_messages() done, defined at D:\Python_Labs\FilmMarketBot\fm_bot.py:31> exception=RuntimeError('Timeout context manager should be used inside a task')>
Traceback (most recent call last):
File "D:\Python_Labs\FilmMarketBot\fm_bot.py", line 37, in get_messages
await message(bot)
File "D:\Python_Labs\FilmMarketBot\bot_files\message_handling.py", line 14, in message
await commands(bot, chat_id)
File "D:\Python_Labs\FilmMarketBot\bot_files\messages\bot_commands\commands.py", line 11, in commands
await start(bot, chat_id)
File "D:\Python_Labs\FilmMarketBot\bot_files\messages\bot_commands\commands_methods.py", line 11, in start
await bot.send_message(chat_id, f"Hello, {first_name}!")
File "D:\Python_Labs\FM_Bot_test\venv\lib\site-packages\aiogram\bot\bot.py", line 339, in send_message
result = await self.request(api.Methods.SEND_MESSAGE, payload)
File "D:\Python_Labs\FM_Bot_test\venv\lib\site-packages\aiogram\bot\base.py", line 231, in request
return await api.make_request(await self.get_session(), self.server, self.__token, method, data, files,
File "D:\Python_Labs\FM_Bot_test\venv\lib\site-packages\aiogram\bot\api.py", line 139, in make_request
async with session.post(url, data=req, **kwargs) as response:
File "D:\Python_Labs\FM_Bot_test\venv\lib\site-packages\aiohttp\client.py", line 1138, in __aenter__
self._resp = await self._coro
File "D:\Python_Labs\FM_Bot_test\venv\lib\site-packages\aiohttp\client.py", line 466, in _request
with timer:
File "D:\Python_Labs\FM_Bot_test\venv\lib\site-packages\aiohttp\helpers.py", line 701, in __enter__
raise RuntimeError(
RuntimeError: Timeout context manager should be used inside a task
This is my fm_bot.py:
# бібліотека request для отримання запитів(повідомлень), які надходять на Flask-сервер від Телеграм-серверу і Stripe
from flask import Flask, request
import logging
from aiogram import Bot, Dispatcher
from threading import Thread
# from dotenv import load_dotenv
from time import sleep
import asyncio
import time
from bot_files.models import db, Users, Films, Purchases
from bot_files.message_handling import message, callback_query, object, other_messages
# from bot_files.other import technical_works
from bot_files.chat_member import delete_user
from bot_files.stripe import create_stripe_webhook
from bot_files.objects.stripe_requests_files.purchases import add_purchase
from bot_files.config import *
logging.basicConfig(level=logging.INFO)
bot = Bot(token=bot_token)
dp = Dispatcher(bot)
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = db_url
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
db.init_app(app)
async def get_messages():
print("Flag")
print(request.json)
message_type = list(request.json.keys())[1]
if message_type == 'message':
await message(bot)
elif message_type == 'callback_query':
await callback_query(bot, payment_token, stripe_token)
elif message_type == 'my_chat_member':
chat_id = request.json["my_chat_member"]["chat"]["id"]
if request.json["my_chat_member"]["new_chat_member"]["status"] == "kicked":
await delete_user(chat_id)
elif message_type == 'object':
await object(bot)
elif message_type == 'pre_checkout_query':
# Номер карти - 4242 4242 4242 4242
chat_id = request.json["pre_checkout_query"]["from"]["id"]
await add_purchase(bot, chat_id)
pre_checkout_query_id = request.json["pre_checkout_query"]["id"]
await bot.answer_pre_checkout_query(pre_checkout_query_id, True)
# else:
# await other_messages(bot)
return {"get_messages is ok": True}
async def sleep_func():
await asyncio.sleep(20)
print("Hello!")
#app.route('/' + bot_token, methods=['POST'])
async def asynchrony():
#start_time = time.time()
#get_messages_task = asyncio.create_task(get_messages())
#sleep_task1 = asyncio.create_task(sleep_func())
#sleep_task2 = asyncio.create_task(sleep_func())
#await asyncio.wait([get_messages_task, sleep_task1])
#print(time.time() - start_time)
get_messages_task = asyncio.create_task(get_messages())
await asyncio.wait([get_messages_task])
#await get_messages()
return {"asynchrony is ok": True}
#app.route('/')
async def webhook():
await bot.delete_webhook()
await bot.set_webhook(url=app_url)
# chat_id = None
await create_stripe_webhook(app_url, payment_token)
# await test_users()
# await test_films()
# await test_purchases()
# await test_discounts()
# while True:
# time_now = datetime.datetime.now()
# if datetime.date.today().isoweekday() == 4:
# await technical_works(bot)
sleep_task = asyncio.create_task(sleep_func())
await asyncio.wait([sleep_task])
return '!', 200
if __name__ == "__main__":
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))
I will be very appreciative for you help!
My answer is not suitable for this code, which is Flask-app. But it is answer for this question. So you must use Aiogram for asynchrony receiving and processing messages. Aiogram has special decorator async_task for this: https://docs.aiogram.dev/en/latest/_modules/aiogram/dispatcher/dispatcher.html#Dispatcher.async_task.
If your bot at the same time must perform another function, for example, change data in database once a day, you can create button only for administrator that will start infinity cycle which will sleep for number of seconds in one day.
I am trying to run the following test:
tests.py
from rest_framework.test import APITestCase
from myapp.routing import application
from channels.testing import WebsocketCommunicator
from account.models import User
from rest_framework.authtoken.models import Token
class Tests(APITestCase):
def setUp(self):
self.user = User.objects.create(email='test#test.test',
password='a password')
self.token, created = Token.objects.get_or_create(user=self.user)
async def test_connect(self):
communicator = WebsocketCommunicator(application, f"/ws/user/{self.token}/")
connected, subprotocol = await communicator.connect()
self.assertTrue(connected)
await communicator.disconnect()
application is a boilerplate instance of channels.routing.ProtocolTypeRouter (like in here: https://channels.readthedocs.io/en/latest/topics/routing.html). Everything works fine in production. The test exits with the following error:
Traceback (most recent call last):
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/testing.py", line 74, in receive_output
return await self.output_queue.get()
File "/usr/lib/python3.7/asyncio/queues.py", line 159, in get
await getter
concurrent.futures._base.CancelledError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/sync.py", line 223, in __call__
return call_result.result()
File "/usr/lib/python3.7/concurrent/futures/_base.py", line 428, in result
return self.__get_result()
File "/usr/lib/python3.7/concurrent/futures/_base.py", line 384, in __get_result
raise self._exception
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/sync.py", line 292, in main_wrap
result = await self.awaitable(*args, **kwargs)
File "/home/projects/myapp/myapp-api/app/tests.py", line 35, in test_connect
connected, subprotocol = await communicator.connect()
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/channels/testing/websocket.py", line 36, in connect
response = await self.receive_output(timeout)
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/testing.py", line 85, in receive_output
raise e
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/testing.py", line 74, in receive_output
return await self.output_queue.get()
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/timeout.py", line 66, in __aexit__
self._do_exit(exc_type)
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/timeout.py", line 103, in _do_exit
raise asyncio.TimeoutError
concurrent.futures._base.TimeoutError
----------------------------------------------------------------------
Ran 1 test in 1.026s
I have tried python versions 3.7.5, 3.8.0 and 3.9.9 using channels 3.0.4 with django 3.2.10 and channels-redis 3.3.1 ('BACKEND': 'channels_redis.core.RedisChannelLayer' in settings.py). The error persists. What am I doing wrong?
I had the same problem. APITestCase or TestCase dont allow transactions, you have to use SimpleTestCase from django test, and set databases to all. Just with that difference i think it will work.
Note that the transactions will be saved between test, and not rolled back after the test.
from django.test import SimpleTestCase
from myapp.routing import application
from channels.testing import WebsocketCommunicator
from account.models import User
from rest_framework.authtoken.models import Token
class Tests(SimpleTestCase):
databases = '__all__'
def setUp(self):
self.user = User.objects.create(email='test#test.test', password='a password')
self.token, created = Token.objects.get_or_create(user=self.user)
async def test_connect(self):
communicator = WebsocketCommunicator(application, f"/ws/user/{self.token}/")
connected, subprotocol = await communicator.connect()
self.assertTrue(connected)
await communicator.disconnect()
here are the information of SimpleTestCase
https://docs.djangoproject.com/en/4.0/topics/testing/tools/
I faced a similar issue and the solution is usually to mimic your production router for the tests too, i.e whatever middleware or additional component used in production should also be added when imstantiating your Communicator. for example in my asgi.py I have:
application = ProtocolTypeRouter(
{
"http": get_asgi_application(),
"websocket": AllowedHostsOriginValidator(
jwt_auth_middleware_stack(URLRouter(chat.routing.websocket_urlpatterns)),
),
}
)
My communicator is instantiated as follows:
communicator = WebsocketCommunicator(jwt_auth_middleware_stack(URLRouter(websocket_urlpatterns)),
f"/ws/chat/{chat.id}/?token={token}")
And my url is:
websocket_urlpatterns = [
path("ws/chat/<str:chat_id>/", consumers.AsyncChatConsumer.as_asgi())
]
I did a chatting part in my web app with django it works perfectly in local but after I deployed it to heroku , it doesn't work:
this is my consumers.py class:
from channels.generic.websocket import AsyncWebsocketConsumer
from django.contrib.auth.models import User
from django.db.models import Q
from asgiref.sync import sync_to_async
import json
from chat.models import Thread, Message
from users.serializers import UserSerializer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
friend = None
me = self.scope['user'] # logged in user
friend_name = self.scope['url_route']['kwargs']['friend'] # get the username of that user, whoom you want to chat
friend_instance = await sync_to_async(User.objects.get, thread_sensitive=True)(username=friend_name) # get user object of friend
# create a new Thread object if thread of specific chat does not exists, otherwise return the thread
thread = None
try:
thread = await sync_to_async(Thread.objects.get, thread_sensitive=True)((Q(user1=me) & Q(user2=friend_instance)) | (Q(user1=friend_instance) & Q(user2=me)))
except:
thread = await sync_to_async(Thread.objects.create, thread_sensitive=True)(user1=me, user2=friend_instance)
self.room_name = thread.room_name # room name
await self.channel_layer.group_add(
self.room_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
'''
disconnect the websocket connection.
'''
await self.channel_layer.group_discard (
self.room_name,
self.channel_name
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
from_user = text_data_json['user']
to_user = text_data_json['friend']
from_user_instanse = await sync_to_async(User.objects.get, thread_sensitive=True)(username=from_user['username']) # get user object of friend
to_user_instanse = await sync_to_async(User.objects.get, thread_sensitive=True)(username=to_user['username']) # get user object of friend
thread_obj = await sync_to_async(Thread.objects.get, thread_sensitive=True)((Q(user1=from_user_instanse) & Q(user2=to_user_instanse)) | (Q(user1=to_user_instanse) & Q(user2=from_user_instanse)))
message_instane = await sync_to_async(Message.objects.create, thread_sensitive=True)(messag_body=message, from_user=from_user_instanse, to_user=to_user_instanse, thread=thread_obj)
await self.channel_layer.group_send(
self.room_name,
{
'type': 'chatroom_messages',
'message': message_instane.messag_body,
'user': message_instane.from_user
}
)
async def chatroom_messages(self, event):
message = event['message']
user = event['user']
user_serialized_data = UserSerializer(user)
await self.send(text_data=json.dumps({
'message': message,
'user': user_serialized_data.data
}))
in the template this is the connection request part with js:
chatSocket.send(JSON.stringify({ //the error shows in this part that WebSocket is already in CLOSING or CLOSED state
'message': msg,
'user': me,
'friend': friendName
}));
....
/* connection request */
const chatSocket = new WebSocket(
'wss://'
+ window.location.host
+ '/ws/chat/'
+ friendName['username']
+ '/'
);
in my chat app this I have routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<friend>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
in my project I have also routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing
application = ProtocolTypeRouter ({
'websocket': AuthMiddlewareStack(
URLRouter (
chat.routing.websocket_urlpatterns
)
)
})
and this is actually my asgi.py :
import os
import django #added
#from django.core.asgi import get_asgi_application #removed
from channels.routing import get_default_application # added
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
django.setup() #added
application = get_default_application() #added
#application = get_asgi_application() #removed
in my procfile :
release: python manage.py migrate
web: daphne myproject.asgi:application --port $PORT --bind 0.0.0.0 -v2
worker: python manage.py runworker channels --settings=myproject.settings -v2
in my settings.py
ASGI_APPLICATION = "myproject.routing.application"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [os.environ.get('REDIS_URL', 'redis://localhost:6379')],
},
},
}
it works perfectly in local but not anymore working after deployement, am I messing something
this is heroku logs :
......
2021-12-29T18:15:42.187913+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packa
ges/aioredis/connection.py", line 111, in create_connection
2021-12-29T18:15:42.187913+00:00 app[web.1]: reader, writer = await asyncio.wait_for(open_conne
ction(
2021-12-29T18:15:42.187916+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/ta
sks.py", line 442, in wait_for
2021-12-29T18:15:42.187916+00:00 app[web.1]: return await fut
2021-12-29T18:15:42.187916+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packa
ges/aioredis/stream.py", line 23, in open_connection
2021-12-29T18:15:42.187916+00:00 app[web.1]: transport, _ = await get_event_loop().create_conne
ction(
2021-12-29T18:15:42.187917+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/ba
se_events.py", line 1056, in create_connection
2021-12-29T18:15:42.187917+00:00 app[web.1]: raise exceptions[0]
2021-12-29T18:15:42.187917+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/ba
se_events.py", line 1041, in create_connection
2021-12-29T18:15:42.187917+00:00 app[web.1]: sock = await self._connect_sock(
2021-12-29T18:15:42.187917+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/ba
se_events.py", line 955, in _connect_sock
2021-12-29T18:15:42.187918+00:00 app[web.1]: await self.sock_connect(sock, address)
2021-12-29T18:15:42.187918+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/se
lector_events.py", line 502, in sock_connect
2021-12-29T18:15:42.187918+00:00 app[web.1]: return await fut
2021-12-29T18:15:42.187918+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/se
lector_events.py", line 537, in _sock_connect_cb
2021-12-29T18:15:42.187919+00:00 app[web.1]: raise OSError(err, f'Connect call failed {address}
')
2021-12-29T18:15:42.187919+00:00 app[web.1]: ConnectionRefusedError: [Errno 111] Connect call f
ailed ('127.0.0.1', 6379)
2021-12-29T18:15:42.188173+00:00 app[web.1]: 2021-12-29 18:15:42,188 INFO failing WebSocket
opening handshake ('Internal server error')
2021-12-29T18:15:42.188439+00:00 app[web.1]: 2021-12-29 18:15:42,188 WARNING dropping connecti
on to peer tcp4:10.1.93.62:26296 with abort=False: Internal server error
2021-12-29T18:15:42.189132+00:00 app[web.1]: 2021-12-29 18:15:42,189 DEBUG WebSocket closed
for ['10.1.93.62', 26296]
2021-12-29T18:15:42.189195+00:00 app[web.1]: 10.1.93.62:26296 - - [29/Dec/2021:18:15:42] "WSDIS
CONNECT /ws/chat/person/" - -
I am attempting to use Django Channels on my digital ocean ubuntu 18 droplet to serve a 'video' feed from a webcam attached to a raspberry pi. The raspberry pi is using python websockets-client and openCV to send jpeg images quickly (every 0.05 seconds).
The issue is that this will work great, streaming for 10+ minutes perfectly with good frame rate, and I will think I have got it working. Then the next day, it will stream for about 10 seconds before the frame rate on the browser becomes very laggy and then stops receiving images. The Raspberry pi then disconnects from the websocket from what appears to be my server disconnecting it, I cannot get it to stream longer than a few seconds. Why would it work perfectly one day, and not the next? Nothing obvious has changed.
The reason I think it is my server/channels/redis causing the problem:
Before the raspberry pi even seems to be having any trouble, the video feed being shown on the browser will become very laggy then will stop getting new images entirely. Then a few seconds later my raspberry pi socket will disconnect from the websocket.
What I have Tried:
I have done a speed test on my internet connection and it is 50mb down/15mb up.
I have tried reducing the frame rate to something very slow, like one image per second, and it will still stop working.
I have checked on my memory and CPU of both my droplet and raspberry pi. When a crash occurs, CPU on both machines are typically below 25% usage, and memory is typically below 40% usage.
I changed some of the redis settings in settings.py shown below, did not seem to have any effect.
What would cause an intermittently functioning websocket? I have changed my redis settings in settings.py, but I am not sure what else to change or attempt to debug.
I followed the tutorial on deployment here: Channels Docs
Channels code:
Consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer
#import urllib.parse as urlparse
#from urllib.parse import parse_qs
from channels.db import database_sync_to_async
from accounts.models import CustomUser
from rest_framework.authtoken.models import Token
#websocket for video connection
class StreamConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['device_id']
print(self.room_name)
self.room_group_name = 'stream_%s' % self.room_name
self.token_passed = self.scope['query_string'].decode() #token passed in with ws://url/?token
self.token_actual = await self.get_token(self.room_name) #go get actual token for this monitor
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
#check to ensure tokens match, if they do, allow connection
if self.token_passed == self.token_actual:
await self.accept()
print('True')
else:
print(self.token_passed)
print(self.token_actual)
await self.close()
async def disconnect(self, close_code):
# Leave room group
print(close_code)
print("closing")
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
#database_sync_to_async
def get_token(self, monitor_serial):
try:
monitor = CustomUser.objects.get(deviceSerial=monitor_serial)
token = Token.objects.get(user=monitor)
return token.key
except:
return "user not available"
# Receive message from WebSocket
async def receive(self, bytes_data=None, text_data=None):
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'stream_data',
'bytes_data': bytes_data,
'text_data': text_data
}
)
# Receive message from room group
async def stream_data(self, event):
bytes_data = event['bytes_data']
text_data = event['text_data']
# Send message to WebSocket
await self.send(bytes_data=bytes_data, text_data=text_data)
class StatusConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['device_id']
self.room_group_name = 'status_%s' % self.room_name
self.token_passed = self.scope['query_string'].decode() #token passed in with ws://url/?token
self.token_actual = await self.get_token(self.room_name) #go get actual token for this monitor
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
#check to ensure tokens match, if they do, allow connection
if self.token_passed == self.token_actual:
await self.accept()
else:
print(self.token_passed)
print(self.token_actual)
await self.close()
async def disconnect(self, close_code):
# Leave room group
print(close_code)
print("closing")
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
#database_sync_to_async
def get_token(self, monitor_serial):
monitor = CustomUser.objects.get(deviceSerial=monitor_serial)
token = Token.objects.get(user=monitor)
return token.key
# Receive message from WebSocket
async def receive(self, text_data=None):
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'status_data',
'text_data': text_data
}
)
# Receive message from room group
async def status_data(self, event):
#bytes_data = event['bytes_data']
text_data = event['text_data']
# Send message to WebSocket
#await self.send(bytes_data=bytes_data, text_data=text_data)
await self.send(text_data=text_data)
asgi.py
import os
from django.conf.urls import url
from django.core.asgi import get_asgi_application
# Fetch Django ASGI application early to ensure AppRegistry is populated
# before importing consumers and AuthMiddlewareStack that may import ORM
# models.
try:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "polysensesite.settings")
except:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
django_asgi_app = get_asgi_application()
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from .consumers import StreamConsumer, StatusConsumer
application = ProtocolTypeRouter({
# Django's ASGI application to handle traditional HTTP requests
"http": django_asgi_app,
# WebSocket chat handler
"websocket": AuthMiddlewareStack(
URLRouter([
url(r"^stream/(?P<device_id>[\d\-]+)/$", StreamConsumer.as_asgi()),
url(r"^status/(?P<device_id>[\d\-]+)/$", StatusConsumer.as_asgi()),
])
),
})
settings.py
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("localhost", 6379)],
"capacity": 5000,
"expiry": 5,
},
},
}
I put some print statements on the browser, consumers, and raspberry pi, and it seems like everything is working except the raspberry pi stops receiving messages (but is still attempting to send images) then the socket closes abruptly and I get this message from Daphne:
127.0.0.1:37154 - - [30/May/2021:16:56:32] "WSDISCONNECT /stream/XXXXXXX/" - -
1001
closing
2021-05-30 16:56:33,364 ERROR Exception inside application:
Traceback (most recent call last):
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/routing.py", line 71, in __call__
return await application(scope, receive, send)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/sessions.py", line 47, in __call__
return await self.inner(dict(scope, cookies=cookies), receive, send)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/sessions.py", line 254, in __call__
return await self.inner(wrapper.scope, receive, wrapper.send)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/auth.py", line 181, in __call__
return await super().__call__(scope, receive, send)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/middleware.py", line 26, in __call__
return await self.inner(scope, receive, send)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/routing.py", line 160, in __call__
send,
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/consumer.py", line 94, in app
return await consumer(scope, receive, send)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/consumer.py", line 59, in __call__
[receive, self.channel_receive], self.dispatch
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/utils.py", line 51, in await_many_dispatch
await dispatch(result)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/consumer.py", line 73, in dispatch
await handler(message)
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/generic/websocket.py", line 198, in websocket_receive
await self.receive(bytes_data=message["bytes"])
File "./polysensesite/consumers.py", line 69, in receive
'text_data': text_data
File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels_redis/core.py", line 667, in group_send
key, min=0, max=int(time.time()) - int(self.expiry)
aioredis.errors.ConnectionForcedCloseError
I'm using Django==1.9.2 and djangorestframework==3.3.2, and django.test.Client to make some tests. The problem is that when I execute my tests I'm gettting this error:
ERROR: test_view (main.tests.test_http.TestMainViewSet)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/vladir/work/all/project-django1.9/saxo-publish/publish/main/tests/test_http.py", line 111, in test_view
content_type='application/json'
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 515, in post
secure=secure, **extra)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 314, in post
secure=secure, **extra)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 380, in generic
return self.request(**r)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 449, in request
response = self.handler(environ)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 123, in __call__
response = self.get_response(request)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 245, in get_response
response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 296, in handle_uncaught_exception
return callback(request, **param_dict)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/utils/decorators.py", line 166, in _wrapped_view
return middleware.process_response(request, response)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/middleware/csrf.py", line 230, in process_response
request.META["CSRF_COOKIE"],
KeyError: u'CSRF_COOKIE'
My test code looks like this:
import json
from django.test import Client
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
class TestMainViewSet(TestCase):
def setUp(self):
self.client = Client(
HTTP_HOST='example.com' # I have also tried removing this
)
self.create_read_url = reverse('books-list')
User.objects.create_user(
username="username",
email="username#zunzun.se",
password="123"
)
def test_create(self):
self.client.login(username='username', password="123")
# In this case I'm doing a POST, but it is the same with a GET
response = self.client.post(
self.create_read_url,
data=json.dumps({'title': "Create"}), # I have also tried without the json.dumps
content_type='application/json'
)
data = json.loads(response.content)
print data
self.assertEqual(response.status_code, 201)
self.assertEquals(data['title'], "Create")
my view code is:
from django.contrib.auth.mixins import LoginRequiredMixin
from rest_framework import viewsets
from .serialiazers import (
BookSerializerRead,
BookSerializerWrite,
)
class MainViewSet(LoginRequiredMixin, viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class_read = BookSerializerRead
serializer_class_write = BookSerializerWrite
on the urls.py:
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'books', MainViewSet, 'books')
urlpatterns = [
url(r'^api/', include(router.urls)),
]
According with the Django doc about it, I should not need anything additional to avoid the CSRF checks,
because as textually said there: "By default, the test client will disable any CSRF checks performed by your site.", and I also know that enforce_csrf_checks=False by default
on the Client.
I have found one detail though, if I create an instance of the client that way self.client = Client(HTTP_HOST='example.com', CSRF_COOKIE='xxxxxx') then it works, but is that
actually needed? It is not what the documentation says, so I suppose I'm doing something wrong. Could someone help me with that please? I will appreciate any help about.
Thanks in advance
Try to use DRF's APITestCase as base class for test cases:
from rest_framework.testing import APITestCase
class TestMainViewSet(APITestCase):
...