Simple way to test websocket availability in python - python

I am using the following code to test that a local websocket server is running:
import asyncio
import websockets
async def hello():
async with websockets.connect('ws://127.0.0.1:8000/ws/registration/123') as websocket:
await websocket.send(json_data)
asyncio.get_event_loop().run_until_complete(hello())
Is there a simpler way to do this without using asyncio? Something such as:
import asyncio
import websockets
conn = websockets.connect('ws://127.0.0.1:8000/ws/registration/123')
conn.send('hello')
Basically, I'm just trying to find the simplest way to test to see if my websocket server is listening and receiving messages at a particular url.

Doesn't async_to_sync make this more complex? Why not just create a normal test_url function:
def test_url(url, data=""):
async def inner():
async with websockets.connect(url) as websocket:
await websocket.send(data)
return asyncio.get_event_loop().run_until_complete(inner())
test_url("ws://127.0.0.1:8000/ws/registration/123")

You can do the above by using async_to_sync, for example:
from asgiref.sync import async_to_sync
import websockets
def test_url(url, data=""):
conn = async_to_sync(websockets.connect)(url)
async_to_sync(conn.send)(data)
test_url("ws://127.0.0.1:8000/ws/registration/123")
Note that the "handshake" will probably not complete here because it needs to be accepted both ways, but the above should enable you to test to make sure that the urls are being routed properly, etc.

Related

FastApi communication with other Api

I am using fastapi very recently and as an exercise I want to connect my fastapi api with a validation service on other server... but I do not know how to do this, I have not found something that will help me in the official documentation.. Will I have to do it with python code? Or is there a way?
FastApi docs
thank you for your help and excuse my english.
The accepted answer certainly works, but it is not an effective solution. With each request, the ClientSession is closed, so we lose the advantage [0] of ClientSession: connection pooling, keepalives, etc. etc.
We can use the startup and shutdown events [1] in FastAPI, which are triggered when the server starts and shuts down respectively. In these events it is possible to create a ClientSession instance and use it during the runtime of the whole application (and therefore utilize its full potential).
The ClientSession instance is stored in the application state. [2]
Here I answered a very similar question in the context of the aiohttp server: https://stackoverflow.com/a/60850857/752142
from __future__ import annotations
import asyncio
from typing import Final
from aiohttp import ClientSession
from fastapi import Depends, FastAPI
from starlette.requests import Request
app: Final = FastAPI()
#app.on_event("startup")
async def startup_event():
setattr(app.state, "client_session", ClientSession(raise_for_status=True))
#app.on_event("shutdown")
async def shutdown_event():
await asyncio.wait((app.state.client_session.close()), timeout=5.0)
def client_session_dep(request: Request) -> ClientSession:
return request.app.state.client_session
#app.get("/")
async def root(
client_session: ClientSession = Depends(client_session_dep),
) -> str:
async with client_session.get(
"https://example.com/", raise_for_status=True
) as the_response:
return await the_response.text()
[0] https://docs.aiohttp.org/en/stable/client_reference.html
[1] https://fastapi.tiangolo.com/advanced/events/
[2] https://www.starlette.io/applications/#storing-state-on-the-app-instance
You will need to code it with Python.
If you're using async you should use a HTTP client that is also async, for example aiohttp.
import aiohttp
#app.get("/")
async def slow_route():
async with aiohttp.ClientSession() as session:
async with session.get("http://validation_service.com") as resp:
data = await resp.text()
# do something with data

Python3 and asyncio: how to implement websocket server as asyncio instance?

I have multiple servers, each server is instance returning by asyncio.start_server. I need my web_server to works with websockets, to have possibility getting data using my javascript client. As I can see, asyncio do not provide websockets, only tcp sockets. Maybe I missed something ? I want to implement websocket server that I can using in asyncio.gather like below:
loop = asyncio.get_event_loop()
login_server = LoginServer.create()
world_server = WorldServer.create()
web_server = WebServer.create()
loop.run_until_complete(
asyncio.gather(
login_server.get_instance(),
world_server.get_instance(),
web_server.get_instance()
)
)
try:
loop.run_forever()
except KeyboardInterrupt:
pass
loop.close()
I do not want to use aiohttp cause if using like in code above aiohttp just blocks another tasks. I need something that will be non-blocking and that will have access to data of another servers (login and world). Does it possible with asyncio ? Does asyncio provide something like websockets ? How to implement websocket server for using in asyncio.gather ?
Well, finally I've implemented WebServer for using in another thread with asyncio. The code (WebServer code):
from aiohttp import web
class WebServer(BaseServer):
def __init__(self, host, port):
super().__init__(host, port)
#staticmethod
async def handle_connection(self, request: web.web_request):
ws = web.WebSocketResponse()
await ws.prepare(request)
async for msg in ws:
Logger.debug('[Web Server]: {}'.format(msg))
return ws
#staticmethod
def run():
app = web.Application()
web.run_app(app, host=Connection.WEB_SERVER_HOST.value, port=Connection.WEB_SERVER_PORT.value)
And how to run:
executor = ProcessPoolExecutor()
loop.run_until_complete(
asyncio.gather(
login_server.get_instance(),
world_server.get_instance(),
loop.run_in_executor(executor, WebServer.run)
)
)

push with python + asyncio + websockets = missing messages

I'm trying to create a websocket server on which a single client will push its messages (I know this is not the usual way to use websocket but this part is not my choice). To this end, I'm using python 3.7 and websockets 7.0.
My issue is: Numerous messages pushed by the client are not received by the server.
Here is the simple code I'm using.
import asyncio
import websockets
async def get_tag_data(websocket, path):
# print('received')
data = await websocket.recv()
print(data)
loop = asyncio.get_event_loop()
anchors_server = websockets.serve(get_tag_data, 'localhost', 9001)
loop.run_until_complete(asyncio.gather(anchors_server))
loop.run_forever()
Conversely, when I try the python-websocket-server (which uses threads for reception), all my messages are correctly received.
As far as I understand asyncio & websockets, it is supposed to manage backpressure: messages sent while the server is busy (to process older messages) are "buffered" and will be treat shortly....
What am I missing? Do I need to thread the websocket reception with asyncio to avoid losing messages?
Thank you for your answers!
Ok, I get it.
My function was running only once, next messages was not buffered. The following code resolve the issue :
import asyncio
import websockets
import signal
import sys
async def get_tag_data(websocket, path):
while True:
async for data in websocket:
print(data)
loop = asyncio.get_event_loop()
anchors_server = websockets.serve(get_tag_data, '', 9001)
loop.run_until_complete(asyncio.gather(anchors_server))
loop.run_forever()
note the
while True:
async for data in websocket
Shoud be without while loop.
import asyncio
import websockets
import signal
import sys
async def get_tag_data(websocket, path):
async for data in websocket:
print(data)

How to receive data from multiple WebSockets asynchronously in Python?

I am currently trying to use the websockets library. If another library is better suited for this purpose, let me know.
Given these functions:
def handle_message(msg):
# do something
async def consumer_handler(websocket, path):
async for message in websocket:
handle_message(message)
How can I (indefinitely) connect to multiple websockets? Would the below code work?
import asyncio
import websockets
connections = set()
connections.add(websockets.connect(consumer_handler, 'wss://api.foo.com', 8765))
connections.add(websockets.connect(consumer_handler, 'wss://api.bar.com', 8765))
connections.add(websockets.connect(consumer_handler, 'wss://api.foobar.com', 8765))
async def handler():
await asyncio.wait([ws for ws in connections])
asyncio.get_event_loop().run_until_complete(handler())
For anyone who finds this, I found an answer. Only works in > Python 3.6.1 I believe.
import asyncio
import websockets
connections = set()
connections.add('wss://api.foo.com:8765')
connections.add('wss://api.bar.com:8765'))
connections.add('wss://api.foobar.com:8765'))
async def handle_socket(uri, ):
async with websockets.connect(uri) as websocket:
async for message in websocket:
print(message)
async def handler():
await asyncio.wait([handle_socket(uri) for uri in connections])
asyncio.get_event_loop().run_until_complete(handler())
Instead of:
connections = set()
I would list it:
connections = []
connections = ["wss://api.foo.com:8765"]
connections.append ("wss://api.bar.com:8765")
connections.append ("wss://api.foobar.com:8765")

Python Tornado: how can I make this asynchronous ?

In the below code, when I do message_response.get(), it will make this particular code synchronous. Now is there a way I can make this aynchronous? Just push the code to a broker. And once celery worker is done with the task, I can write the result back to the client?
import tornado.websocket
from celery_main import do_something_celery_task
class HomePageRequestHandler(tornado.websocket.WebSocketHandler):
def on_message(self, message):
message_response = do_something_celery_task.apply_async((message,))
# How can this be a non blocking call?
self.write_message(message_response.get())
def open(self):
pass
You should try something like this: https://github.com/mher/tornado-celery
You'll have code like this, but I don't run it.
from tornado.websocket import WebSocketHandler
class WebSocketBase(WebSocketHandler):
#gen.coroutine
def on_message(self, message):
response = yield gen.Task(tasks.sleep.apply_async, args=[3])
self.write_message(str(response.result))

Categories