-
-
Notifications
You must be signed in to change notification settings - Fork 908
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Websockets] RuntimeError: Cannot call "receive" once a disconnect message has been received. #2617
Comments
@alex-oleshkevich is there any update on this? |
What's the expected behavior here? |
from what i understand, it is not disconnecting the websocket but the websocket disconnect event is fired. wont raising cause a runtime error? |
I haven't had a chance to look at it yet. |
Steps to reproduce # examples/test.py
import asyncio
import anyio
from starlette.applications import Starlette
from starlette.endpoints import WebSocketEndpoint
from starlette.routing import WebSocketRoute, Route
from starlette.websockets import WebSocket
from starlette.responses import HTMLResponse
class RoomChatWebsocket(WebSocketEndpoint):
encoding = "json"
async def on_connect(self, websocket: WebSocket):
await websocket.accept()
await websocket.send_json({})
async def sender():
while True:
await asyncio.sleep(1)
print("sender exit")
async def on_message_received(websocket: WebSocket):
async for data in websocket.iter_json():
print("------------ got message", data)
async with anyio.create_task_group() as task_group:
async def receiver():
await on_message_received(websocket)
task_group.cancel_scope.cancel()
print("cancelled")
task_group.start_soon(receiver)
task_group.start_soon(sender)
async def on_disconnect(self, websocket: WebSocket, close_code: int):
print(f"Disconnected: {websocket}")
def index_route(request):
return HTMLResponse('<script>ws = new WebSocket("http://localhost:8000/ws")</script>')
routes = [
Route("/", index_route),
WebSocketRoute("/ws", RoomChatWebsocket),
]
app = Starlette(
debug=True,
routes=routes,
)
The problem disappears when I remove I also observe a weird behavior. On |
@alex-oleshkevich whats even weirder is no amount of if checks for websocket state before the loop iteration helps. |
This is not an issue. Use the |
Hi, kindly check the guide written on https://github.com/encode/broadcaster Could you please edit the code in the guide mentioned on broadcaster page and tell me what should the code look like? |
@Kludex ? |
No. I cannot. I don't maintain that project. |
@Kludex you closed this issue after stating a vague reply that only alex could understand with no further help? @alex-oleshkevich could you please tell what changes to make in code since your guide for broadcaster contains code with issues? |
@laxuscullen , Kludex refers to this method The broadcaster's documentation is correct - it demos usage with function-style code. You use class-based endpoints. class RoomChatWebsocket(WebSocketEndpoint):
encoding = "json"
async def on_connect(self, websocket: WebSocket):
await websocket.accept()
async def on_receive(self, websocket: WebSocket, data):
# do something with received data
async def on_disconnect(self, websocket: WebSocket, close_code: int):
print('disconnected') or use function-style code as broadcaster suggests. |
@alex-oleshkevich I am not sure how do I use the subscriber methods or the sender method. I am not able to figure it out. Your help is appreciated for this class based approach. I prefer class based since I have method decorators code written based on class. I refactored my code and I am able to use the function based approach. Also in the function based approach code, there is no on_disconnect? |
@laxuscullen here it is import asyncio
import os
from starlette.applications import Starlette
from starlette.endpoints import WebSocketEndpoint
from starlette.routing import Route, WebSocketRoute
from starlette.templating import Jinja2Templates
from starlette.websockets import WebSocket
from broadcaster import Broadcast
BROADCAST_URL = os.environ.get("BROADCAST_URL", "memory://")
broadcast = Broadcast(BROADCAST_URL)
templates = Jinja2Templates("example/templates")
async def homepage(request):
template = "index.html"
context = {"request": request}
return templates.TemplateResponse(template, context)
class ChatRoomWebsocket(WebSocketEndpoint):
async def on_connect(self, websocket: WebSocket):
await websocket.accept()
self._listener_task = asyncio.create_task(self.chatroom_ws_sender(websocket))
async def on_receive(self, websocket: WebSocket, data):
await broadcast.publish(channel="chatroom", message=data)
async def chatroom_ws_sender(self, websocket) -> None:
async with broadcast.subscribe(channel="chatroom") as subscriber:
async for event in subscriber:
await websocket.send_text(event.message)
async def on_disconnect(self, websocket: WebSocket, close_code: int) -> None:
if self._listener_task:
self._listener_task.cancel()
routes = [
Route("/", homepage),
WebSocketRoute("/", ChatRoomWebsocket, name="chatroom_ws"),
]
app = Starlette(
routes=routes,
on_startup=[broadcast.connect],
on_shutdown=[broadcast.disconnect],
) |
@alex-oleshkevich first of all, i am very thankful to your for your time and help. Really. The code works. Tested using gunicorn (tested with 10 w and 10 t) and uvicorn. |
As recommended by Alex and confirmed that this seems to be an issue with starlette and not broadcaster, I am creating this issue.
This RuntimeError shows on client disconnect.
I have made a sample code which can reproduce the error at https://github.com/laxuscullen/WS/tree/wo_broadcaster
Important
The text was updated successfully, but these errors were encountered: