Skip to content

verse-pbc/websocket_builder

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

29 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

WebSocket Builder

Crates.io Documentation License: MIT

A simple, trait-based WebSocket framework for Rust with Axum integration.

Features

  • 🎯 Simple trait-based API - Just implement 3 methods: on_connect, on_message, on_disconnect
  • πŸ”Œ Per-connection handlers - Each connection gets its own handler instance for natural state management
  • πŸš€ Axum integration - Easy setup with Axum web framework
  • πŸ“¦ Minimal dependencies - Only what's needed for WebSocket handling
  • πŸ›‘οΈ Connection limits - Built-in support for limiting concurrent connections
  • πŸ”„ Automatic protocol handling - Ping/pong and close frames handled automatically

Installation

[dependencies]
websocket_builder = "1.1.0-alpha.1"

Quick Start

use websocket_builder::{WebSocketHandler, HandlerFactory, DisconnectReason, Utf8Bytes, websocket_route};
use axum::extract::ws::{Message, WebSocket};
use futures_util::{stream::SplitSink, SinkExt};
use std::net::SocketAddr;
use anyhow::Result;

// Define your connection handler
struct MyHandler {
    addr: SocketAddr,
    sink: Option<SplitSink<WebSocket, Message>>,
}

impl WebSocketHandler for MyHandler {
    async fn on_connect(
        &mut self,
        addr: SocketAddr,
        sink: SplitSink<WebSocket, Message>,
    ) -> Result<()> {
        self.addr = addr;
        self.sink = Some(sink);
        
        // Send welcome message
        if let Some(sink) = &mut self.sink {
            sink.send(Message::Text("Welcome!".into())).await?;
        }
        Ok(())
    }
    
    async fn on_message(&mut self, text: Utf8Bytes) -> Result<()> {
        // Echo the message back
        if let Some(sink) = &mut self.sink {
            sink.send(Message::Text(format!("Echo: {}", text).into())).await?;
        }
        Ok(())
    }
    
    async fn on_disconnect(&mut self, reason: DisconnectReason) {
        println!("Client {} disconnected: {:?}", self.addr, reason);
    }
}

// Factory to create handler instances
struct MyHandlerFactory;

impl HandlerFactory for MyHandlerFactory {
    type Handler = MyHandler;
    
    fn create(&self, _headers: &axum::http::HeaderMap) -> Self::Handler {
        MyHandler {
            addr: "0.0.0.0:0".parse().unwrap(),
            sink: None,
        }
    }
}

// Create your Axum app
#[tokio::main]
async fn main() {
    let app = websocket_route("/ws", MyHandlerFactory);
    
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Examples

Check out the examples directory for more complete examples:

  • echo_server.rs - Simple echo server
  • chat_server.rs - Multi-user chat room with usernames
  • flexible_handler.rs - Same route handling both HTTP and WebSocket

Run an example:

cargo run --example echo_server

How It Works

  1. Create a handler - Implement the WebSocketHandler trait with your connection logic
  2. Create a factory - Implement HandlerFactory to create handler instances for each connection
  3. Set up routes - Use websocket_route() to create an Axum router with your WebSocket endpoint
  4. Handle connections - Each connection gets its own handler instance, perfect for maintaining state

Configuration

You can configure connection limits and timeouts:

use websocket_builder::{websocket_route_with_config, ConnectionConfig};
use std::time::Duration;

let config = ConnectionConfig {
    max_connections: Some(1000),           // Maximum concurrent connections
    max_connection_duration: Some(Duration::from_secs(300)), // 5 minute max duration
    idle_timeout: Some(Duration::from_secs(60)),            // 1 minute idle timeout
};

let app = websocket_route_with_config("/ws", MyHandlerFactory, config);

Timeout Behavior

  • max_connection_duration: Hard limit on connection lifetime. Connection will be closed after this duration regardless of activity.
  • idle_timeout: Connection closes after this duration of no messages. Resets on any incoming message (text, binary, ping/pong).
  • When both timeouts are set, the shorter one takes effect
  • Timeout disconnections trigger on_disconnect with DisconnectReason::Timeout(message)

Advanced Usage

Flexible Handler Pattern

You can create routes that handle both regular HTTP and WebSocket requests using Option<WebSocketUpgrade>:

use websocket_builder::{WebSocketUpgrade, handle_upgrade};
use axum::{
    extract::ConnectInfo,
    response::{Html, IntoResponse, Response}
};

async fn flexible_handler(
    ConnectInfo(addr): ConnectInfo<SocketAddr>,
    headers: axum::http::HeaderMap,
    ws: Option<WebSocketUpgrade>,
) -> Response {
    match ws {
        Some(ws) => {
            // Handle WebSocket upgrade
            let handler = MyHandlerFactory.create(&headers);
            handle_upgrade(ws, addr, handler).await
        }
        None => {
            // Handle regular HTTP request
            Html("<h1>Hello from HTTP!</h1>").into_response()
        }
    }
}

See the flexible_handler.rs example for a complete implementation that serves both HTML and WebSocket on the same route.

Using handle_socket Directly

For even more control, you can use handle_socket directly with Axum's WebSocket:

use axum::extract::ws::WebSocketUpgrade;
use axum::response::Response;

async fn ws_handler(
    ws: WebSocketUpgrade,
    ConnectInfo(addr): ConnectInfo<SocketAddr>,
) -> Response {
    let handler = factory.create();
    ws.on_upgrade(move |socket| async move {
        websocket_builder::handle_socket(socket, addr, handler, config).await
    })
}

Design Philosophy

This library prioritizes simplicity over flexibility. If you need:

  • Complex middleware chains
  • Multiple message types
  • Binary message handling
  • Custom protocol extensions

You might want to use the underlying Axum WebSocket functionality directly for more control.

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published