[github](https://github.com/tpisto/socketcluster_server) [crates.io](https://crates.io/crates/socketcluster_server) [docs.rs](https://docs.rs/socketcluster_server) [tests](https://github.com/tpisto/socketcluster_server/actions?query=branch%3Amain) # Rust SocketCluster protocol V1 server library A scalable SocketCluster WebSocket server implementation as a library, built with Rust using Axum and Tokio. ## Features - 🚀 WebSocket-based real-time communication - 📡 Publish/Subscribe pattern for efficient message distribution - 🔌 Customizable middleware for packet processing - 🔐 JWT Authentication support - 💓 Ping/Pong mechanism for connection health monitoring - 🌐 HTTP endpoints integration alongside WebSocket functionality - ⚙️ Flexible configuration using TOML files ## Table of Contents - [Installation](#installation) - [Quick Start](#quick-start) - [Configuration](#configuration) - [Usage Examples](#usage-examples) - [Setting up the Server](#setting-up-the-server) - [Implementing Custom Middleware](#implementing-custom-middleware) - [HTTP Endpoint for Publishing Messages directly](#http-endpoint-for-publishing-messages-directly) - [API Documentation](#api-documentation) - [Contributing](#contributing) - [License](#license) ## Installation Add this to your `Cargo.toml`: ```toml [dependencies] socketcluster_server = "0.1.0" ``` ## Quick Start Here's a minimal example to get a server up and running: ```rust use socketcluster_server::{create_socketcluster_state, ServerConfig, WebSocketSender, ws_handler}; use axum::{Router, routing::get}; use tokio::net::TcpListener; use std::net::SocketAddr; #[tokio::main] async fn main() { // Load configuration let config = ServerConfig { ping_interval: std::time::Duration::from_secs(30), ping_timeout: std::time::Duration::from_secs(5), port: 8080, host: "127.0.0.1".to_string(), jwt_secret: "your-secret-key".to_string(), }; // Create application state let state = create_socketcluster_state::(config.clone()); // Set up router let app = Router::new() .route("/ws", get(ws_handler)) .with_state(state); // Start the server let addr = format!("{}:{}", config.host, config.port); let listener = TcpListener::bind(&addr).await.unwrap(); println!("Server listening on: {}", addr); axum::serve(listener, app.into_make_service_with_connect_info::()).await.unwrap(); } ``` ## Configuration The server can be configured using a TOML file. Create a `config/settings.toml` file with the following structure: ```toml ping_interval = 30 # in seconds ping_timeout = 5 # in seconds port = 8080 host = "127.0.0.1" jwt_secret = "your-secret-key" ``` ## Usage Examples ### HTTP Endpoint for Publishing Messages directly This server is a library, so you can use it directly with Axum. Here you can find an simple example how you can directly operate the socketcluster channels and publish, just using Axum route endpoints. ```rust // ... use ... // Axum HTTP handler to send messages directly to the channels async fn send_handler( State(state): State>, TypedHeader(auth_header): TypedHeader>, Json(payload): Json>, ) -> impl IntoResponse { // JWT validation logic here... let channel = payload.get("channel").unwrap().as_str().unwrap(); let message = payload.get("message").unwrap(); if let Some(subscribers) = state.subscriptions.read().await.get(channel) { let publish_event = Packet { event: Some(Event::Publish), data: Some(message.clone()), ..Default::default() }; for sub_socket_id in subscribers { if let Some(socket_data) = state.sockets.read().await.get(sub_socket_id) { let mut sender = socket_data.sender.lock().await; let _ = sender.send(AxumMessage::Text(serde_json::to_string(&publish_event).unwrap())).await; } } (StatusCode::OK, json!({ "ok": true }).to_string()).into_response() } else { (StatusCode::NOT_FOUND, json!({ "ok": false, "message": "channel not found" }).to_string()).into_response() } } #[tokio::main] async fn main() { let settings = config::Config::builder() .add_source(config::File::with_name("config/settings.toml")) .build() .expect("Configuration loading failed"); let config: ServerConfig = settings.clone().try_deserialize().expect("Failed to deserialize configuration"); let mut sc_state = create_socketcluster_state::(config.clone()); // Add routes and start the server let app = Router::new() // Main socketcluster client handler (websocket connection) .route("/ws/", get(ws_handler)) // Axum POST handler to allow sending messages to channels directly .route("/publish_to_channel", post(send_handler)) .with_state(sc_state); let addr = format!("{}:{}", config.host, config.port); let listener = TcpListener::bind(&addr).await.unwrap(); axum::serve(listener, app.into_make_service_with_connect_info::()).await.unwrap(); } ``` ### Implementing Custom Middleware ```rust struct LoggingMiddleware; #[async_trait] impl Middleware for LoggingMiddleware { async fn handle(&self, packet: &mut Packet, _auth_data: &AuthData) -> bool { println!("Processing packet: {:?}", packet); true // Allow the packet to proceed } } // In your main function: let logging_middleware = Arc::new(LoggingMiddleware); sc_state.add_middleware(logging_middleware); ``` ## API Documentation For detailed API documentation, run: ``` cargo doc --open ``` or go to https://docs.rs/socketcluster_server/latest/socketcluster_server/ ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.