product-os-server 0.0.55

Product OS : Server provides a full functioning advanced server capable of acting as a web server, command and control distributed network, authentication server, crawling server and more. Fully featured with high level of flexibility.
Documentation
//! WebSocket Example
//!
//! This example demonstrates how to handle WebSocket connections.

use product_os_server::ProductOSServer;

#[cfg(not(feature = "ws"))]
use product_os_server::{StatusCode, Response, Body};
use product_os_server::ServerConfig;
use product_os_async_executor::TokioExecutor;

#[cfg(feature = "ws")]
use axum::{
    extract::ws::{WebSocketUpgrade, WebSocket, Message},
    response::IntoResponse,
};

#[cfg(feature = "ws")]
use futures::{sink::SinkExt, stream::{StreamExt, SplitSink, SplitStream}};

#[cfg(feature = "ws")]
async fn ws_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
    ws.on_upgrade(handle_socket)
}

#[cfg(feature = "ws")]
async fn handle_socket(mut socket: WebSocket) {
    while let Some(msg) = socket.recv().await {
        let msg = if let Ok(msg) = msg {
            msg
        } else {
            // client disconnected
            return;
        };

        if socket.send(msg).await.is_err() {
            // client disconnected
            return;
        }
    }
}

#[cfg(feature = "ws")]
async fn ws_concurrent_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
    ws.on_upgrade(handle_socket_concurrent)
}

#[cfg(feature = "ws")]
async fn handle_socket_concurrent(socket: WebSocket) {
    let (sender, receiver) = socket.split();

    tokio::spawn(write_messages(sender));
    tokio::spawn(read_messages(receiver));
}

#[cfg(feature = "ws")]
async fn read_messages(mut receiver: SplitStream<WebSocket>) {
    while let Some(Ok(msg)) = receiver.next().await {
        println!("Received message: {:?}", msg);
    }
}

#[cfg(feature = "ws")]
async fn write_messages(mut sender: SplitSink<WebSocket, Message>) {
    use tokio::time::{interval, Duration};
    
    let mut interval = interval(Duration::from_secs(1));
    
    for i in 0..10 {
        interval.tick().await;
        let msg = Message::Text(format!("Server message {}", i).into());
        if sender.send(msg).await.is_err() {
            break;
        }
    }
}

#[cfg(not(feature = "ws"))]
async fn fallback_handler() -> Result<Response<Body>, StatusCode> {
    Ok(Response::builder()
        .status(StatusCode::NOT_IMPLEMENTED)
        .body(Body::from("WebSocket feature not enabled"))
        .unwrap())
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize tracing
    tracing_subscriber::fmt::init();

    // Create configuration
    let mut config = ServerConfig::new();
    config.network.port = 3000;

    println!("Creating WebSocket server...");
    
    // Create server
    let mut server: ProductOSServer<(), TokioExecutor, _> = 
        ProductOSServer::new_with_config(config);

    #[cfg(feature = "ws")]
    {
        // Add WebSocket routes
        server.add_ws_handler("/ws", ws_handler);
        server.add_ws_handler("/ws/concurrent", ws_concurrent_handler);
        
        println!("WebSocket Server listening on http://localhost:3000");
        println!("Try connecting to:");
        println!("  ws://localhost:3000/ws");
        println!("  ws://localhost:3000/ws/concurrent");
        println!("");
        println!("Example with websocat:");
        println!("  websocat ws://localhost:3000/ws");
    }

    #[cfg(not(feature = "ws"))]
    {
        server.add_get("/", fallback_handler);
        println!("WebSocket feature not enabled. Rebuild with --features ws");
    }

    // Start server
    server.start(false).await.map_err(|e| format!("Server error: {}", e))?;

    // Keep the main thread alive
    loop {
        tokio::time::sleep(std::time::Duration::from_secs(3600)).await;
    }
}