websocket handshake fails with django channels - websocket

EDIT: correction after #Ken4scholars comment below
I have the following consumer which fails right after connecting
consumers.py
from channels.generic.websocket import AsyncJsonWebsocketConsumer
#...
class ListGeneratedTokensByFileConsumer(AsyncJsonWebsocketConsumer):
stop = False
async def websocket_connect(self,event):
await self.accept()
self.stop = False
async def websocket_receive(self,event):
await self.send_json({"text":"received","accept": True})
await self.send_tokens_list()
async def websocket_disconnect(self,event):
self.stop = True
async def send_tokens_list(self):
some_path = "..."
while self.stop == False:
await asyncio.sleep(2)
the_message = {}
if os.path.isfile("some_file.json")):
with open(os.path.join(some_path ,"some_file.json"),'r') as new_tok:
the_message = json.load(new_tok)
if not the_message:
print("waiting...")
else:
await self.send_json(the_message)
await self.close()
It always throws the error: ERR_CONNECTION:RESEST and the websocket disconnects with code 1006. This might seem familiar to recent changes in django-channels but since I am sending a text once the websocket opens and send a message back from the consumer it should do the trick. Or is there something wrong?
routing.py
url(r'^myapp/sub_path/(?P<pk>\d+)/sub_sub_path/',ListGeneratedTokensByFileConsumer)
and the websocket endpoint in js is:
.js
var loc = window.location;
var wsStart = "ws://";
if (loc.protocol == "https:") {
wsStart = "wss://";
}
var endpoint = wsStart + loc.host + loc.pathname + "sub_sub_path" + "/";
for info, with channels-redis==2.3.2, channels==2.3.0, asgiref==3.2.2, daphne==2.3.0, django==2.0.8

If you see something like in django logs:
WebSocket HANDSHAKING /ws/your_route
WebSocket REJECT /ws/your_route
WebSocket DISCONNECT /ws/your_route
And you wrapped websocket router with AllowedHostsOriginValidator in the asgi.py like:
application = ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": AllowedHostsOriginValidator(
URLRouter(
chat_websocket_urlpatterns
))
})
Then you definetely should check the ALLOWED_HOSTS variable in the settings.py, maybe you forgot to put something in there. In my case it was just because I didn't specify my IP address, only localhost.
This is most likely not that the asker looked for, but I thought it may come handy to someone else, as there is not much info about the django channels in this site.

Related

My function does not execute when using await in a async function pyscript

I try to fetch data from my api in python using pyscript. Following the pyscript documentation I use the async keyword on my main function and use asyncio.ensure_future to execute it, everything before the first await work but not the await keyword and any other line of code after it.
This is my code:
async def request(url: str,
method: str = "GET",
body: Optional[str] = None,
headers: Optional[dict[str, str]] = None,
**fetch_kwargs: Any) -> FetchResponse:
kwargs = {
"method": method,
"mode": "no-cors"
} # CORS: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
if body and method not in ["GET", "HEAD"]:
kwargs["body"] = body
if headers:
kwargs["headers"] = headers
kwargs.update(fetch_kwargs)
response = await pyfetch(url, **kwargs)
return response
async def get_barycenter(
filename: str,
base_url: str = "http://localhost:8001") -> dict[str, Any] | None:
headers = {"Content-type": "application/json"}
response = await request(f"{base_url}/barycenter/?image_name={filename}",
headers=headers)
body = await response.json()
if body.status != 200:
return None
return body.msg
async def main():
print('start')
test = await get_barycenter("img.jpg")
print(test)
print("end")
asyncio.ensure_future(main())
The result is only the print of start and nothing else no print of test are even "end".
I tested the API the data is visible in Insomnia and I set up correctly the cors Policy.
Part of the issue here is an existing issue in PyScript where exceptions raised in Coroutines aren't displayed on the page. To help with this for now, I would recommend adding the following snippet before your request function:
import js
def handler(loop, context):
js.console.error(context.message)
raise(context.exception)
pyscript.loop.set_exception_handler(handler)
This way, exceptions raised in coroutines are displayed in the browser's console log.
What the root issue of the the fetch request is I couldn't say, but at least this will get errors displaying and help you troubleshoot. For example, when I run your code, I see:
GET http://localhost:8001/barycenter/?image_name=img.jpg net::ERR_CONNECTION_REFUSED
Since I don't have a local server running - hopefully the errors that appear for you are more helpful.

Websocket running in asyncio loop inside a thread disconnects immediately

I'm developing an application that uses multiple threads for various communication channels. One of them is websocket for which I'm using asyncio loop to run:
class Rotator():
def __init__(self):
# some irrelevant stuff here...
self._readThread = threading.Thread(target=self._ReadThread, daemon=True)
self._usbThread = threading.Thread(target=self._UsbThread, daemon=True)
self._webThread = threading.Thread(target=self._WebThread, daemon=True)
self._socketThread = threading.Thread(target=self._SocketThread, daemon=True)
self.InitializeRotator()
def InitializeRotator(self):
# some more irrelevant stuff here
self._readThread.start()
self._usbThread.start()
self._webThread.start()
self._socketThread.start()
# not including _ReadThread() and _UsbThread() as they are irrelevant in this context
def _WebThread(self): # including _WebThread() as illustration
handler_object = server.WebHttpRequestHandler
_httpServer = socketserver.TCPServer(("",self.webPort), handler_object)
_httpServer.serve_forever()
async def ws_handler(websocket, path):
data = await websocket.recv()
reply = f"Data recieved as: {data}!"
await websocket.send(reply)
def _SocketThread(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
start_server = websockets.serve(self.ws_handler, str(self.DHCPIP), 8000)
loop.run_until_complete(start_server)
loop.run_forever()
The frontend JS code that communicates with this is:
var ws;
function startWebsocket() {
ws = new WebSocket('ws://192.168.200.76:8000')
ws.onopen = function(e){
console.log('Connection established')
ws.send('Connection established')
}
ws.onmessage = function(e){
console.log('websocket message event:', e)
}
ws.onclose = function(){
// connection closed, discard old websocket and create a new one in 5s
console.log('Connection lost, will reconnect in 5s...')
ws = null
setTimeout(startWebsocket, 5000)
}
}
startWebsocket();
The web page that contains the frontend is served by the server run by _WebThread(). The issue is that the connection is being closed immediately after being established. I'm assuming the issue is caused by some conflict in the asyncio/thread combination, because I tested the server side code on its own and it worked like a charm. What am I doing wrong?
I recreated your example and managed to fix it using two changes:
ws_handler is missing the self parameter. Since it's an instance method, the instance itself will be passed as the first parameter. The actual websocket object will be the second one.
in ws_handler I added a while True loop. I followed the example in the docs here https://websockets.readthedocs.io/en/stable/intro/tutorial1.html#bootstrap-the-server
final result:
async def ws_handler(self, websocket, path):
while True:
data = await websocket.recv()
reply = f"Data recieved as: {data}!"
await websocket.send(reply)
Other changed that I made were to use 8000 as a port for the http server, 8001 for the websocket server and 'localhost' instead of 192.168.200.76. But I'm pretty sure that's not relevant

Downloading File in Parallelwith Asyncio

I trying to pull Avro files from an API link while using Asyncio. Currently it just returns nothing if the link is to an avro file - while all my other API calls which pull json data work. What am I missing?
credentials = {'authorization': XXXXX}
async def get_data(link, session,creds)-> None:
async with session.get(url, url=link, headers=credential) as res:
content = await res.read()
r = await session.request('GET', url=str(link), headers=creds)
data = await r
return
async def data_distributor_function(credential)-> None:
async with aiohttp.ClientSession() as session:
link_list = ["https://.....","https://.....","https://.....","https://.....","https://....."]
tasks = []
for link in link_list:
tasks.append(wait_for(get_data( link=link, session=session,creds=credential),timeout=10))
results = await asyncio.gather(*tasks, return_exceptions=True)
return
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
data = asyncio.run(data_distributor_function(credential),debug=True)
If I don't do the API call in asyncio, I can just use a standard request and it works (it's just slow).
reply = requests.request("GET", link, credentials)

Python Socket.io event handling

I'm a complete beginner when it comes to socket, so please bear with me if the question seems too trivial for you.
The following is a code that i found on GitLab and I'm trying to understand
import os
import logging
import uuid
import socketio
from aiohttp import web
import sys
sys.path.append('.')
logging.basicConfig(level=logging.WARN,
format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
datefmt='%m-%d %H:%M')
# Home page
async def index(request):
index_file = open('examples/rasa_demo/templates/index.html')
return web.Response(body=index_file.read().encode('utf-8'), headers={'content-type': 'text/html'})
# Action endpoint
async def webhook(request):
"""Webhook to retrieve action calls."""
action_call = await request.json()
try:
response = await executor.run(action_call)
except ActionExecutionRejection as e:
logger.error(str(e))
response = {"error": str(e), "action_name": e.action_name}
response.status_code = 400
return response
return web.json_response(response)
# Web app routing
app = web.Application()
app.add_routes([
web.get('/', index),
web.post('/webhook', webhook),
web.static('/static', 'examples/rasa_demo/static')
])
# Instantiate all bot agents
bots = BotFactory.createAll()
# Websocket through SocketIO with support for regular HTTP endpoints
sio = socketio.AsyncServer(async_mode='aiohttp', cors_allowed_origins='*')
sio.attach(app)
#sio.on('session_request')
async def on_session_request(sid, data):
if data is None:
data = {}
if 'session_id' not in data or data['session_id'] is None:
data['session_id'] = uuid.uuid4().hex
await sio.emit('session_confirm', data['session_id'])
#sio.on('user_uttered')
async def on_user_uttered(sid, message):
custom_data = message.get('customData', {})
lang = custom_data.get('lang', 'en')
user_message = message.get('message', '')
bot_responses = await bots[lang].handle_text(user_message) #await BotFactory.getOrCreate(lang).handle_text(user_message)
for bot_response in bot_responses:
json = __parse_bot_response(bot_response)
await sio.emit('bot_uttered', json, room=sid)
What I'm trying to understand is how do the event handlers catch or events like 'session_request' or'user_uttered' when they were never emitted.
Thank you.

aiohttp websocket and redis pub/sub

i created a simple websocket server using aiohttp . my server reads message from redis pub/sub and sends it to client .
this is my websocket code:
import aiohttp
from aiohttp import web
import aioredis
router = web.RouteTableDef()
#router.get("/ws")
async def websocket_handler(request):
ws = web.WebSocketResponse()
await ws.prepare(request)
sub = request.config_dict["REDIS"]
ch, *_ = await sub.subscribe('hi')
async for msg in ch.iter(encoding='utf-8'):
await ws.send_str('{}: {}'.format(ch.name, msg))
async def init_redis(app):
redis_pool = await aioredis.create_redis_pool('redis://localhost')
app["REDIS"] = redis_pool
yield
redis_pool.close()
await redis_pool.wait_closed()
async def init_app():
app = web.Application()
app.add_routes(router)
app.cleanup_ctx.append(init_redis)
return app
web.run_app(init_app())
my first client can connect to server and receive messages but when i create another client to connect to this endpoint it receive no messages !
what is the problem ? how can i fix this problem?
You need to call create_redis for each client and publish the message to the channel. Otherwise, only the first client will receive the subscribed message.
So, you can edit your code as follows.
import aiohttp
from aiohttp import web
import aioredis
import asyncio
router = web.RouteTableDef()
async def reader(ws, ch):
while (await ch.wait_message()):
await ws.send_str('{}: {}'.format(ch.name, msg))
#router.get("/ws")
async def websocket_handler(request):
ws = web.WebSocketResponse()
await ws.prepare(request)
sub = await aioredis.create_redis_pool('redis://localhost')
pub = await aioredis.create_redis_pool('redis://localhost')
ch, *_ = await sub.subscribe('hi')
asyncio.ensure_future(reader(ws, ch))
async for msg in ws:
await pub.publish('hi', msg)
sub.close()
pub.close()
async def init_app():
app = web.Application()
app.add_routes(router)
return app
web.run_app(init_app())
Please note there may be minor syntax error (e.g. format of the message), but this is the structure that you should follow as it worked for me. I got it to work with my application.

Resources