# # This file is part of gunicorn released under the MIT license. # See the NOTICE for more information. """ WebSocket ASGI application example. Run with: gunicorn -k asgi examples.asgi.websocket_app:app Test with: # Using websocat (install with: cargo install websocat) websocat ws://127.0.0.1:8000/ws # Or using Python websockets library python -c " import asyncio import websockets async def test(): async with websockets.connect('ws://127.0.0.1:8000/ws') as ws: await ws.send('Hello') print(await ws.recv()) asyncio.run(test()) " """ async def app(scope, receive, send): """ASGI application with WebSocket support.""" if scope["type"] == "lifespan": await handle_lifespan(scope, receive, send) elif scope["type"] == "http": await handle_http(scope, receive, send) elif scope["type"] == "websocket": await handle_websocket(scope, receive, send) else: raise ValueError(f"Unknown scope type: {scope['type']}") async def handle_lifespan(scope, receive, send): """Handle lifespan events.""" while True: message = await receive() if message["type"] == "lifespan.startup": await send({"type": "lifespan.startup.complete"}) elif message["type"] == "lifespan.shutdown": await send({"type": "lifespan.shutdown.complete"}) return async def handle_http(scope, receive, send): """Handle HTTP requests - serve a simple HTML page for WebSocket testing.""" path = scope["path"] if path == "/": html = HTML_PAGE.encode() await send({ "type": "http.response.start", "status": 200, "headers": [ (b"content-type", b"text/html"), (b"content-length", str(len(html)).encode()), ], }) await send({ "type": "http.response.body", "body": html, }) else: await send({ "type": "http.response.start", "status": 404, "headers": [(b"content-type", b"text/plain")], }) await send({ "type": "http.response.body", "body": b"Not Found", }) async def handle_websocket(scope, receive, send): """Handle WebSocket connections.""" path = scope["path"] if path == "/ws": await echo_websocket(scope, receive, send) elif path == "/ws/chat": await chat_websocket(scope, receive, send) else: # Reject the connection await send({"type": "websocket.close", "code": 4004}) async def echo_websocket(scope, receive, send): """Echo WebSocket - sends back whatever it receives.""" # Wait for connection message = await receive() if message["type"] != "websocket.connect": return # Accept the connection await send({"type": "websocket.accept"}) # Echo loop try: while True: message = await receive() if message["type"] == "websocket.disconnect": break if message["type"] == "websocket.receive": if "text" in message: # Echo text back await send({ "type": "websocket.send", "text": f"Echo: {message['text']}" }) elif "bytes" in message: # Echo bytes back await send({ "type": "websocket.send", "bytes": message["bytes"] }) except Exception as e: print(f"WebSocket error: {e}") finally: try: await send({"type": "websocket.close", "code": 1000}) except Exception: pass async def chat_websocket(scope, receive, send): """Chat WebSocket - simple broadcast example.""" message = await receive() if message["type"] != "websocket.connect": return await send({ "type": "websocket.accept", "subprotocol": "chat" }) await send({ "type": "websocket.send", "text": "Welcome to the chat! Send messages and they will be echoed back." }) try: while True: message = await receive() if message["type"] == "websocket.disconnect": break if message["type"] == "websocket.receive" and "text" in message: text = message["text"] await send({ "type": "websocket.send", "text": f"[You]: {text}" }) except Exception: pass HTML_PAGE = """ WebSocket Test

WebSocket Test

"""