capnweb_server/
ws_h1.rs

1use crate::Server;
2use axum::{
3    extract::{
4        ws::{Message as WsMessage, WebSocket, WebSocketUpgrade},
5        State,
6    },
7    response::Response,
8};
9use futures::{SinkExt, StreamExt};
10use std::sync::Arc;
11use tracing::{debug, error, info, warn};
12
13/// WebSocket handler for Cap'n Web protocol
14pub async fn websocket_handler(
15    ws: WebSocketUpgrade,
16    State(server): State<Arc<Server>>,
17) -> Response {
18    ws.on_upgrade(move |socket| handle_socket(socket, server))
19}
20
21async fn handle_socket(socket: WebSocket, _server: Arc<Server>) {
22    let session_id = uuid::Uuid::new_v4().to_string();
23    info!("WebSocket connection established: {}", session_id);
24
25    let (mut sender, mut receiver) = socket.split();
26
27    // Handle incoming messages
28    while let Some(result) = receiver.next().await {
29        match result {
30            Ok(msg) => {
31                match msg {
32                    WsMessage::Text(text) => {
33                        debug!("Received text message: {}", text);
34
35                        // LEGACY MESSAGE FORMAT - NOT SUPPORTED IN WIRE PROTOCOL
36                        // The official Cap'n Web protocol uses newline-delimited arrays only
37                        // WebSocket support will need to be reimplemented with wire protocol
38                        error!("WebSocket handler uses legacy Message format which is no longer supported");
39                        error!("Only the official Cap'n Web wire protocol (newline-delimited arrays) is supported");
40
41                        // Send error response
42                        let error_msg = "WebSocket support requires wire protocol implementation";
43                        if let Err(e) = sender
44                            .send(WsMessage::Text(error_msg.to_string().into()))
45                            .await
46                        {
47                            error!("Failed to send error response: {}", e);
48                            break;
49                        }
50                    }
51                    WsMessage::Binary(data) => {
52                        warn!(
53                            "Received binary message, Cap'n Web over WebSocket expects text/JSON"
54                        );
55                        // Try to decode as UTF-8 and process as text
56                        if let Ok(text) = String::from_utf8(data.to_vec()) {
57                            debug!("Converted binary to text: {}", text);
58                            // Recursively handle as text message
59                            continue;
60                        } else {
61                            error!("Binary message is not valid UTF-8");
62                        }
63                    }
64                    WsMessage::Ping(_) => {
65                        debug!("Received ping, WebSocket will auto-respond with pong");
66                    }
67                    WsMessage::Pong(_) => {
68                        debug!("Received pong");
69                    }
70                    WsMessage::Close(frame) => {
71                        info!("WebSocket closing: {} (reason: {:?})", session_id, frame);
72                        break;
73                    }
74                }
75            }
76            Err(e) => {
77                error!("WebSocket error: {}", e);
78                break;
79            }
80        }
81    }
82
83    // Clean up session
84    // TODO: Add lifecycle management for cleaning up capabilities when available
85
86    info!("WebSocket disconnected: {}", session_id);
87}