forked from cleveragents/cleveragents-core
3c0b326c0d
Each simulation ran the full legacy lifecycle (init → tell → build → apply) with real Anthropic Claude API calls: 1. sim1-todo-cli: Click-based todo app (add/list/delete) 2. sim2-bookstore-api: FastAPI CRUD with SQLite (5 code files) 3. sim3-websocket-chat: async websocket chat server 4. sim4-hn-scraper: HN top stories scraper with JSON output 5. sim5-flask-auth: Flask + Flask-Login authentication app All 5 generated correct, production-quality Python code matching the requested task specifications.
451 lines
15 KiB
Python
451 lines
15 KiB
Python
I'll create a production-ready WebSocket chat server with proper error handling and connection management. Here are the files:
|
|
|
|
## chat_server.py
|
|
```python
|
|
#!/usr/bin/env python3
|
|
"""
|
|
WebSocket Chat Server
|
|
A production-ready chat server supporting multiple concurrent clients.
|
|
"""
|
|
|
|
import asyncio
|
|
import json
|
|
import logging
|
|
import signal
|
|
import sys
|
|
import websockets
|
|
from datetime import datetime
|
|
from typing import Set, Dict, Any
|
|
from websockets.server import WebSocketServerProtocol
|
|
from websockets.exceptions import ConnectionClosed, WebSocketException
|
|
|
|
# Configure logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class ChatServer:
|
|
"""WebSocket chat server managing multiple client connections."""
|
|
|
|
def __init__(self, host: str = "localhost", port: int = 8765):
|
|
self.host = host
|
|
self.port = port
|
|
self.clients: Set[WebSocketServerProtocol] = set()
|
|
self.server = None
|
|
self._shutdown_event = asyncio.Event()
|
|
|
|
async def register_client(self, websocket: WebSocketServerProtocol) -> None:
|
|
"""Register a new client connection."""
|
|
self.clients.add(websocket)
|
|
client_info = f"{websocket.remote_address[0]}:{websocket.remote_address[1]}"
|
|
logger.info(f"Client connected: {client_info} (Total: {len(self.clients)})")
|
|
|
|
# Send welcome message to new client
|
|
welcome_msg = self._create_message(
|
|
"system",
|
|
f"Welcome to the chat! {len(self.clients)} user(s) online."
|
|
)
|
|
await self._safe_send(websocket, welcome_msg)
|
|
|
|
async def unregister_client(self, websocket: WebSocketServerProtocol) -> None:
|
|
"""Unregister a client connection."""
|
|
self.clients.discard(websocket)
|
|
client_info = f"{websocket.remote_address[0]}:{websocket.remote_address[1]}"
|
|
logger.info(f"Client disconnected: {client_info} (Total: {len(self.clients)})")
|
|
|
|
async def broadcast_message(self, message: str, sender: WebSocketServerProtocol) -> None:
|
|
"""Broadcast a message to all connected clients except the sender."""
|
|
if not self.clients:
|
|
return
|
|
|
|
# Create structured message
|
|
chat_message = self._create_message("user", message, sender.remote_address[0])
|
|
|
|
# Send to all clients except sender
|
|
recipients = self.clients - {sender}
|
|
if recipients:
|
|
await asyncio.gather(
|
|
*[self._safe_send(client, chat_message) for client in recipients],
|
|
return_exceptions=True
|
|
)
|
|
logger.info(f"Broadcasted message to {len(recipients)} clients")
|
|
|
|
def _create_message(self, msg_type: str, content: str, sender: str = "system") -> str:
|
|
"""Create a structured JSON message."""
|
|
return json.dumps({
|
|
"type": msg_type,
|
|
"sender": sender,
|
|
"content": content,
|
|
"timestamp": datetime.now().isoformat()
|
|
})
|
|
|
|
async def _safe_send(self, websocket: WebSocketServerProtocol, message: str) -> None:
|
|
"""Safely send a message to a client with error handling."""
|
|
try:
|
|
await websocket.send(message)
|
|
except ConnectionClosed:
|
|
logger.debug(f"Connection closed for {websocket.remote_address}")
|
|
await self.unregister_client(websocket)
|
|
except WebSocketException as e:
|
|
logger.warning(f"WebSocket error sending to {websocket.remote_address}: {e}")
|
|
await self.unregister_client(websocket)
|
|
except Exception as e:
|
|
logger.error(f"Unexpected error sending to {websocket.remote_address}: {e}")
|
|
await self.unregister_client(websocket)
|
|
|
|
async def handle_client(self, websocket: WebSocketServerProtocol, path: str) -> None:
|
|
"""Handle a client connection throughout its lifecycle."""
|
|
await self.register_client(websocket)
|
|
|
|
try:
|
|
async for message in websocket:
|
|
try:
|
|
# Validate message
|
|
if not isinstance(message, str):
|
|
continue
|
|
|
|
message = message.strip()
|
|
if not message or len(message) > 1000: # Message length limit
|
|
continue
|
|
|
|
# Broadcast to other clients
|
|
await self.broadcast_message(message, websocket)
|
|
|
|
except json.JSONDecodeError:
|
|
logger.warning(f"Invalid JSON from {websocket.remote_address}")
|
|
except Exception as e:
|
|
logger.error(f"Error processing message from {websocket.remote_address}: {e}")
|
|
break
|
|
|
|
except ConnectionClosed:
|
|
logger.debug(f"Client {websocket.remote_address} disconnected normally")
|
|
except WebSocketException as e:
|
|
logger.warning(f"WebSocket error for {websocket.remote_address}: {e}")
|
|
except Exception as e:
|
|
logger.error(f"Unexpected error handling client {websocket.remote_address}: {e}")
|
|
finally:
|
|
await self.unregister_client(websocket)
|
|
|
|
async def start(self) -> None:
|
|
"""Start the WebSocket server."""
|
|
logger.info(f"Starting chat server on {self.host}:{self.port}")
|
|
|
|
self.server = await websockets.serve(
|
|
self.handle_client,
|
|
self.host,
|
|
self.port,
|
|
ping_interval=30, # Send ping every 30 seconds
|
|
ping_timeout=10, # Wait 10 seconds for pong
|
|
max_size=10**6, # Max message size: 1MB
|
|
max_queue=32 # Max queued messages per connection
|
|
)
|
|
|
|
logger.info(f"Chat server started successfully on ws://{self.host}:{self.port}")
|
|
|
|
# Wait for shutdown signal
|
|
await self._shutdown_event.wait()
|
|
|
|
async def stop(self) -> None:
|
|
"""Stop the WebSocket server gracefully."""
|
|
logger.info("Shutting down chat server...")
|
|
|
|
if self.server:
|
|
self.server.close()
|
|
await self.server.wait_closed()
|
|
|
|
# Close all client connections
|
|
if self.clients:
|
|
await asyncio.gather(
|
|
*[client.close() for client in self.clients.copy()],
|
|
return_exceptions=True
|
|
)
|
|
|
|
self._shutdown_event.set()
|
|
logger.info("Chat server stopped")
|
|
|
|
def shutdown(self) -> None:
|
|
"""Signal shutdown from sync context."""
|
|
self._shutdown_event.set()
|
|
|
|
async def main():
|
|
"""Main entry point."""
|
|
server = ChatServer(host="0.0.0.0", port=8765)
|
|
|
|
# Setup signal handlers for graceful shutdown
|
|
def signal_handler():
|
|
logger.info("Received shutdown signal")
|
|
server.shutdown()
|
|
|
|
if sys.platform != "win32":
|
|
loop = asyncio.get_event_loop()
|
|
for sig in (signal.SIGTERM, signal.SIGINT):
|
|
loop.add_signal_handler(sig, signal_handler)
|
|
|
|
try:
|
|
await server.start()
|
|
except KeyboardInterrupt:
|
|
logger.info("Received keyboard interrupt")
|
|
except Exception as e:
|
|
logger.error(f"Server error: {e}")
|
|
finally:
|
|
await server.stop()
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
asyncio.run(main())
|
|
except KeyboardInterrupt:
|
|
print("\nShutdown complete.")
|
|
```
|
|
|
|
## requirements.txt
|
|
```txt
|
|
websockets>=11.0.2
|
|
```
|
|
|
|
## README.md
|
|
```markdown
|
|
# WebSocket Chat Server
|
|
|
|
A production-ready WebSocket chat server built with Python that supports multiple concurrent clients and real-time message broadcasting.
|
|
|
|
## Features
|
|
|
|
- **Multi-client Support**: Handle multiple concurrent connections
|
|
- **Real-time Messaging**: Instant message broadcasting to all connected clients
|
|
- **Graceful Connection Management**: Proper handling of client connections and disconnections
|
|
- **Error Handling**: Robust error handling for network issues and malformed messages
|
|
- **Structured Messages**: JSON-formatted messages with timestamps
|
|
- **Resource Limits**: Message size limits and connection management
|
|
- **Logging**: Comprehensive logging for monitoring and debugging
|
|
|
|
## Requirements
|
|
|
|
- Python 3.7+
|
|
- websockets library
|
|
|
|
## Installation
|
|
|
|
1. Clone or download the project files
|
|
2. Install dependencies:
|
|
```bash
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Starting the Server
|
|
|
|
```bash
|
|
python chat_server.py
|
|
```
|
|
|
|
The server will start on `ws://0.0.0.0:8765` by default.
|
|
|
|
### Configuration
|
|
|
|
Modify the server settings in the `main()` function:
|
|
|
|
```python
|
|
server = ChatServer(host="0.0.0.0", port=8765)
|
|
```
|
|
|
|
### Connecting Clients
|
|
|
|
Connect to the WebSocket server using any WebSocket client:
|
|
|
|
```javascript
|
|
const ws = new WebSocket('ws://localhost:8765');
|
|
|
|
ws.onmessage = function(event) {
|
|
const message = JSON.parse(event.data);
|
|
console.log(`[${message.timestamp}] ${message.sender}: ${message.content}`);
|
|
};
|
|
|
|
ws.send("Hello, everyone!");
|
|
```
|
|
|
|
## Message Format
|
|
|
|
The server uses structured JSON messages:
|
|
|
|
```json
|
|
{
|
|
"type": "user|system",
|
|
"sender": "client_ip|system",
|
|
"content": "message_content",
|
|
"timestamp": "2023-12-07T10:30:00.000000"
|
|
}
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
- **No Authentication**: This is a basic implementation without user authentication
|
|
- **Rate Limiting**: Consider implementing rate limiting for production use
|
|
- **Input Validation**: Messages are limited to 1000 characters
|
|
- **Network Security**: Use WSS (WebSocket Secure) in production
|
|
|
|
## Production Deployment
|
|
|
|
For production deployment, consider:
|
|
|
|
1. **Reverse Proxy**: Use nginx or similar for SSL termination
|
|
2. **Process Management**: Use systemd, supervisor, or container orchestration
|
|
3. **Monitoring**: Implement health checks and metrics collection
|
|
4. **Scaling**: Use Redis or similar for multi-server message broadcasting
|
|
|
|
## Graceful Shutdown
|
|
|
|
The server supports graceful shutdown via:
|
|
- `Ctrl+C` (SIGINT)
|
|
- `SIGTERM` signal (Unix-like systems)
|
|
|
|
## Logging
|
|
|
|
Logs include:
|
|
- Client connection/disconnection events
|
|
- Message broadcasting statistics
|
|
- Error conditions and warnings
|
|
- Server startup/shutdown events
|
|
|
|
## License
|
|
|
|
MIT License - Feel free to use and modify as needed.
|
|
```
|
|
|
|
## client_test.html
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>WebSocket Chat Test Client</title>
|
|
<style>
|
|
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
|
|
#messages { border: 1px solid #ccc; height: 300px; overflow-y: auto; padding: 10px; margin: 10px 0; background-color: #f9f9f9; }
|
|
#messageInput { width: 70%; padding: 10px; }
|
|
#sendButton { width: 25%; padding: 10px; }
|
|
.message { margin: 5px 0; }
|
|
.system { color: #666; font-style: italic; }
|
|
.user { color: #000; }
|
|
.status { padding: 10px; margin: 10px 0; border-radius: 5px; }
|
|
.connected { background-color: #d4edda; color: #155724; }
|
|
.disconnected { background-color: #f8d7da; color: #721c24; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>WebSocket Chat Test Client</h1>
|
|
|
|
<div id="status" class="status disconnected">Disconnected</div>
|
|
|
|
<div>
|
|
<button id="connectButton" onclick="connect()">Connect</button>
|
|
<button id="disconnectButton" onclick="disconnect()" disabled>Disconnect</button>
|
|
</div>
|
|
|
|
<div id="messages"></div>
|
|
|
|
<div>
|
|
<input type="text" id="messageInput" placeholder="Type your message here..." disabled>
|
|
<button id="sendButton" onclick="sendMessage()" disabled>Send</button>
|
|
</div>
|
|
|
|
<script>
|
|
let ws = null;
|
|
const messagesDiv = document.getElementById('messages');
|
|
const messageInput = document.getElementById('messageInput');
|
|
const statusDiv = document.getElementById('status');
|
|
const connectButton = document.getElementById('connectButton');
|
|
const disconnectButton = document.getElementById('disconnectButton');
|
|
const sendButton = document.getElementById('sendButton');
|
|
|
|
function connect() {
|
|
try {
|
|
ws = new WebSocket('ws://localhost:8765');
|
|
|
|
ws.onopen = function(event) {
|
|
updateStatus('Connected', true);
|
|
addMessage('Connected to chat server', 'system');
|
|
};
|
|
|
|
ws.onmessage = function(event) {
|
|
try {
|
|
const message = JSON.parse(event.data);
|
|
const timestamp = new Date(message.timestamp).toLocaleTimeString();
|
|
addMessage(`[${timestamp}] ${message.sender}: ${message.content}`, message.type);
|
|
} catch (e) {
|
|
addMessage(event.data, 'user');
|
|
}
|
|
};
|
|
|
|
ws.onclose = function(event) {
|
|
updateStatus('Disconnected', false);
|
|
addMessage('Disconnected from chat server', 'system');
|
|
};
|
|
|
|
ws.onerror = function(error) {
|
|
addMessage('WebSocket error: ' + error, 'system');
|
|
};
|
|
|
|
} catch (error) {
|
|
addMessage('Connection error: ' + error, 'system');
|
|
}
|
|
}
|
|
|
|
function disconnect() {
|
|
if (ws) {
|
|
ws.close();
|
|
}
|
|
}
|
|
|
|
function sendMessage() {
|
|
const message = messageInput.value.trim();
|
|
if (message && ws && ws.readyState === WebSocket.OPEN) {
|
|
ws.send(message);
|
|
addMessage(`You: ${message}`, 'user');
|
|
messageInput.value = '';
|
|
}
|
|
}
|
|
|
|
function addMessage(text, type) {
|
|
const messageElement = document.createElement('div');
|
|
messageElement.className = `message ${type}`;
|
|
messageElement.textContent = text;
|
|
messagesDiv.appendChild(messageElement);
|
|
messagesDiv.scrollTop = messagesDiv.scrollHeight;
|
|
}
|
|
|
|
function updateStatus(text, connected) {
|
|
statusDiv.textContent = text;
|
|
statusDiv.className = `status ${connected ? 'connected' : 'disconnected'}`;
|
|
|
|
connectButton.disabled = connected;
|
|
disconnectButton.disabled = !connected;
|
|
messageInput.disabled = !connected;
|
|
sendButton.disabled = !connected;
|
|
}
|
|
|
|
// Send message on Enter key
|
|
messageInput.addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
sendMessage();
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
This implementation provides a robust, production-ready WebSocket chat server with:
|
|
|
|
1. **Proper connection management** with automatic cleanup
|
|
2. **Error handling** for network issues and malformed data
|
|
3. **Structured JSON messaging** with timestamps
|
|
4. **Resource limits** to prevent abuse
|
|
5. **Graceful shutdown** support
|
|
6. **Comprehensive logging** for monitoring
|
|
7. **Test client** for easy testing
|
|
|
|
The server can handle multiple concurrent clients and broadcasts messages efficiently while maintaining connection stability. |