Router

Struct Router 

Source
pub struct Router { /* private fields */ }
Expand description

The main router for WebSocket servers with middleware support.

Router is the central component that manages routing, middleware, state, connections, and server lifecycle. It uses a builder pattern for configuration and supports both WebSocket and HTTP static file serving on the same port.

§Middleware System

The router supports two types of middleware:

  • Global middleware (added with layer()) - Applied to all routes
  • Per-route middleware (added with route_with_layers()) - Applied to specific routes

Execution order: Global middleware → Per-route middleware → Handler

§Thread Safety

Router is thread-safe and can be cloned cheaply (uses Arc internally). All connections share the same router instance.

§Examples

§Basic Setup

use wsforge::prelude::*;

async fn handler(msg: Message) -> Result<String> {
    Ok("received".to_string())
}

let router = Router::new()
    .default_handler(handler(handler));

router.listen("0.0.0.0:8080").await?;

§With Middleware Layers

use wsforge::prelude::*;

let router = Router::new()
    .layer(LoggerMiddleware::new())           // Global: logs all
    .layer(auth_middleware())                  // Global: auth all
    .route("/public", handler(handler))        // No extra middleware
    .route_with_layers(
        "/admin",
        vec![admin_only_middleware()],         // Per-route: admin only
        handler(handler)
    );

router.listen("127.0.0.1:8080").await?;

Implementations§

Source§

impl Router

Source

pub fn new() -> Self

Creates a new empty router.

The router starts with no routes, no middleware, no state, and no handlers. Use the builder methods to configure it.

§Examples
use wsforge::prelude::*;

let router = Router::new();
Source

pub fn layer(self, middleware: Arc<dyn Middleware>) -> Self

Add a global middleware layer that applies to all routes.

Global middleware are executed before per-route middleware and handlers. They are executed in the order they are added.

§Arguments
  • middleware - The middleware to add
§Examples
§Single Middleware
use wsforge::prelude::*;

let router = Router::new()
    .layer(LoggerMiddleware::new());
§Multiple Middleware
use wsforge::prelude::*;

let router = Router::new()
    .layer(LoggerMiddleware::new())        // First: logging
    .layer(auth_middleware())               // Second: authentication
    .layer(rate_limit_middleware());        // Third: rate limiting
Source

pub fn route(self, path: impl Into<String>, handler: Arc<dyn Handler>) -> Self

Registers a handler for a specific route without additional middleware.

Global middleware will still apply to this route. For route-specific middleware, use route_with_layers().

Routes are matched against the beginning of incoming messages. For example, a message like /chat hello would match route /chat.

§Arguments
  • path - The route path (e.g., “/chat”, “/api/users”)
  • handler - The handler function wrapped with handler()
§Examples
use wsforge::prelude::*;

async fn chat_handler(msg: Message) -> Result<String> {
    Ok("chat response".to_string())
}

async fn api_handler(msg: Message) -> Result<String> {
    Ok("api response".to_string())
}

let router = Router::new()
    .route("/chat", handler(chat_handler))
    .route("/api", handler(api_handler));
Source

pub fn route_with_layers( self, path: impl Into<String>, layers: Vec<Arc<dyn Middleware>>, handler: Arc<dyn Handler>, ) -> Self

Per-route middleware are executed after global middleware but before the handler. This is useful for route-specific concerns like authorization or validation.

§Arguments
  • path - The route path (e.g., “/admin”, “/api/users”)
  • layers - Vector of middleware to apply to this route
  • handler - The handler function wrapped with handler()
§Examples
§Admin Route with Auth
use wsforge::prelude::*;

async fn admin_handler(msg: Message) -> Result<String> {
    Ok("Admin panel".to_string())
}

let router = Router::new()
    .route_with_layers(
        "/admin",
        vec![
            auth_middleware(),
            admin_check_middleware(),
        ],
        handler(admin_handler)
    );
§API Route with Validation
use wsforge::prelude::*;

async fn api_handler(msg: Message) -> Result<String> {
    Ok("API response".to_string())
}

let router = Router::new()
    .route_with_layers(
        "/api/users",
        vec![validate_json_middleware()],
        handler(api_handler)
    );
Source

pub fn with_state<T: Send + Sync + 'static>(self, data: Arc<T>) -> Self

Adds shared state to the router.

State is shared across all connections and can be extracted in handlers using the [State] extractor. Any type that is Send + Sync + 'static can be used as state.

§Type Safety

Multiple different types can be added as state. Each type is stored separately and retrieved by type.

§Arguments
  • data - The state data to share (wrapped in Arc)
§Examples
§Single State
use wsforge::prelude::*;
use std::sync::Arc;

struct Database {
    // database fields
}

async fn handler(State(db): State<Arc<Database>>) -> Result<String> {
    Ok("query result".to_string())
}

let db = Arc::new(Database {});

let router = Router::new()
    .with_state(db)
    .default_handler(handler(handler));
§Multiple States
use wsforge::prelude::*;
use std::sync::Arc;

struct Config {
    port: u16,
}

struct Database {
    // database fields
}

let router = Router::new()
    .with_state(Arc::new(Config { port: 8080 }))
    .with_state(Arc::new(Database {}));
Source

pub fn on_connect<F>(self, f: F) -> Self
where F: Fn(&Arc<ConnectionManager>, ConnectionId) + Send + Sync + 'static,

Sets a callback to be called when a new connection is established.

The callback receives a reference to the connection manager and the connection ID. This is useful for logging, sending welcome messages, or updating user lists.

§Arguments
  • f - Callback function with signature Fn(&Arc<ConnectionManager>, ConnectionId)
§Examples
§Simple Logging
use wsforge::prelude::*;

let router = Router::new()
    .on_connect(|manager, conn_id| {
        println!("New connection: {} (Total: {})", conn_id, manager.count());
    });
§Send Welcome Message
use wsforge::prelude::*;

let router = Router::new()
    .on_connect(|manager, conn_id| {
        if let Some(conn) = manager.get(&conn_id) {
            let _ = conn.send_text("Welcome to the server!");
        }
    });
§Broadcast Join Notification
use wsforge::prelude::*;

let router = Router::new()
    .on_connect(|manager, conn_id| {
        let msg = format!("User {} joined", conn_id);
        manager.broadcast(Message::text(msg));
    });
Source

pub fn on_disconnect<F>(self, f: F) -> Self
where F: Fn(&Arc<ConnectionManager>, ConnectionId) + Send + Sync + 'static,

Sets a callback to be called when a connection is closed.

The callback receives a reference to the connection manager and the connection ID. Note that the connection is already removed from the manager when this is called.

§Arguments
  • f - Callback function with signature Fn(&Arc<ConnectionManager>, ConnectionId)
§Examples
§Logging Disconnections
use wsforge::prelude::*;

let router = Router::new()
    .on_disconnect(|manager, conn_id| {
        println!("Connection {} closed (Remaining: {})", conn_id, manager.count());
    });
§Broadcast Leave Notification
use wsforge::prelude::*;

let router = Router::new()
    .on_disconnect(|manager, conn_id| {
        let msg = format!("User {} left", conn_id);
        manager.broadcast(Message::text(msg));
    });
Source

pub fn default_handler(self, handler: Arc<dyn Handler>) -> Self

Sets the default handler for messages that don’t match any route.

This handler is called when no route matches the incoming message. Global middleware will still be applied to the default handler.

§Arguments
  • handler - The default handler wrapped with handler()
§Examples
§Echo Server
use wsforge::prelude::*;

async fn echo(msg: Message) -> Result<Message> {
    Ok(msg)
}

let router = Router::new()
    .default_handler(handler(echo));
§Error Handler
use wsforge::prelude::*;

async fn not_found() -> Result<String> {
    Ok("Unknown command".to_string())
}

let router = Router::new()
    .route("/known", handler(not_found))
    .default_handler(handler(not_found));
Source

pub fn serve_static(self, path: impl Into<PathBuf>) -> Self

Enables static file serving from a directory.

When enabled, the router will serve static files (HTML, CSS, JavaScript, images) from the specified directory for HTTP requests, while still handling WebSocket connections on the same port.

§Path Resolution
  • Requests to / serve index.html from the directory
  • Other requests map directly to files (e.g., /style.cssdirectory/style.css)
  • MIME types are automatically detected
§Security

Path traversal attempts (e.g., ../../etc/passwd) are automatically blocked.

§Arguments
  • path - Path to the directory containing static files
§Examples
§Serve Static Files
use wsforge::prelude::*;

async fn ws_handler(msg: Message) -> Result<Message> {
    Ok(msg)
}

let router = Router::new()
    .serve_static("public")  // Serve files from ./public
    .default_handler(handler(ws_handler));

// Now you can access:
// http://localhost:8080/          -> public/index.html
// http://localhost:8080/app.js    -> public/app.js
// ws://localhost:8080             -> WebSocket handler
Source

pub fn connection_manager(&self) -> Arc<ConnectionManager>

Returns a reference to the connection manager.

The connection manager is automatically created with the router. Use this to get access to it for storing in state or elsewhere.

§Examples
use wsforge::prelude::*;

let router = Router::new();
let manager = router.connection_manager();

// Now you can use the manager
println!("Active connections: {}", manager.count());
Source

pub async fn listen(self, addr: impl AsRef<str>) -> Result<()>

Starts the WebSocket server and listens for connections.

This method consumes the router and starts the server loop. It will run indefinitely until the process is terminated or an error occurs.

The connection manager is automatically inserted into the router’s state before the server starts, making it available to all handlers via the State<Arc<ConnectionManager>> extractor.

§Arguments
  • addr - The address to bind to (e.g., “127.0.0.1:8080”, “0.0.0.0:3000”)
§Errors

Returns an error if:

  • The address format is invalid
  • The port is already in use
  • Permission is denied (e.g., ports < 1024 on Unix)
§Examples
§Basic Usage
use wsforge::prelude::*;

let router = Router::new();
router.listen("127.0.0.1:8080").await?;
§All Interfaces
use wsforge::prelude::*;

let router = Router::new();
router.listen("0.0.0.0:8080").await?;  // Accept connections from anywhere

Trait Implementations§

Source§

impl Clone for Router

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Default for Router

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for Router

§

impl !RefUnwindSafe for Router

§

impl Send for Router

§

impl Sync for Router

§

impl Unpin for Router

§

impl !UnwindSafe for Router

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more